// Универсальный музыкальный плеер для фоновой музыки const { spawn } = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); class MusicPlayer { constructor() { this.process = null; this.isPlaying = false; this.isPaused = false; this.currentTrack = null; this.volume = 70; // Громкость от 0 до 100 this.duckedVolume = 20; // Приглушенная громкость во время баркера this.isDucked = false; this.repeatMode = false; this.onTrackEnd = null; this.platform = os.platform(); console.log('[MUSIC] Музыкальный плеер инициализирован для платформы:', this.platform); } // Получение списка музыкальных файлов getAvailableTracks() { const musicDir = path.resolve('./music/background'); console.log('[MUSIC] Проверяем папку с музыкой:', musicDir); if (!fs.existsSync(musicDir)) { console.log('[MUSIC] Папка с музыкой не найдена:', musicDir); return []; } try { const files = fs.readdirSync(musicDir); console.log('[MUSIC] Все файлы в папке:', files); const musicFiles = files.filter(file => file.endsWith('.mp3') || file.endsWith('.wav') || file.endsWith('.ogg') ); console.log('[MUSIC] Музыкальные файлы:', musicFiles); const tracks = musicFiles.map(file => ({ id: path.parse(file).name, name: path.parse(file).name, path: path.join(musicDir, file) })); console.log('[MUSIC] Готовый список треков:', tracks); return tracks; } catch (error) { console.error('[MUSIC] Ошибка чтения папки с музыкой:', error); return []; } } // Остановка текущего воспроизведения stop() { if (this.process && this.isPlaying) { console.log('[MUSIC] Останавливаем воспроизведение музыки'); this.process.kill('SIGTERM'); this.process = null; } this.isPlaying = false; this.isPaused = false; this.currentTrack = null; this.isDucked = false; return this; } // Воспроизведение трека play(trackPath, onStart = null, onEnd = null) { console.log('[MUSIC] 🎵 Запуск воспроизведения:', trackPath); console.log('[MUSIC] 📊 Текущее состояние перед запуском:', { isPlaying: this.isPlaying, currentTrack: this.currentTrack, volume: this.volume, repeatMode: this.repeatMode }); // Останавливаем предыдущее воспроизведение this.stop(); console.log('[MUSIC] ⏹️ Предыдущее воспроизведение остановлено'); // Проверяем существование файла if (!fs.existsSync(trackPath)) { console.error('[MUSIC] ❌ Файл не найден:', trackPath); return this; } console.log('[MUSIC] ✅ Файл найден, размер:', fs.statSync(trackPath).size, 'байт'); this.currentTrack = trackPath; this.onTrackEnd = onEnd; // Запускаем воспроизведение console.log('[MUSIC] 🚀 Вызываем _startPlayback'); this._startPlayback(trackPath, onStart, onEnd); return this; } // Внутренний метод запуска воспроизведения _startPlayback(trackPath, onStart, onEnd) { console.log('[MUSIC] 🎛️ _startPlayback вызван для:', trackPath); console.log('[MUSIC] 🖥️ Платформа:', this.platform); let command, args; const currentVolume = this.isDucked ? this.duckedVolume : this.volume; console.log('[MUSIC] 🔊 Громкость:', currentVolume); // Выбираем команду в зависимости от ОС if (this.platform === 'linux') { // Используем mpg123 для MP3 или aplay для WAV if (trackPath.endsWith('.mp3')) { command = 'mpg123'; args = ['-g', Math.floor(currentVolume / 10), trackPath]; // mpg123 использует gain от 0 до 10 } else { command = 'aplay'; args = [trackPath]; } } else if (this.platform === 'win32') { // Для Windows используем PowerShell MediaPlayer command = 'powershell'; const escapedPath = trackPath.replace(/\\/g, '\\\\').replace(/"/g, '""'); args = ['-c', ` Add-Type -AssemblyName presentationCore; $mediaPlayer = New-Object system.windows.media.mediaplayer; $mediaPlayer.open('${escapedPath}'); $mediaPlayer.Volume = ${currentVolume / 100}; $mediaPlayer.Play(); while($mediaPlayer.Position -lt $mediaPlayer.NaturalDuration) { Start-Sleep -Milliseconds 100; } `]; } else if (this.platform === 'darwin') { // Для macOS используем afplay command = 'afplay'; args = ['-v', currentVolume / 100, trackPath]; } else { console.error('[MUSIC] Неподдерживаемая ОС:', this.platform); return; } console.log('[MUSIC] 🚀 Выполняем команду:', command); console.log('[MUSIC] 📝 Аргументы (первые 2):', args.slice(0, 2)); // Запускаем процесс console.log('[MUSIC] 🎯 Вызываем spawn...'); this.process = spawn(command, args); console.log('[MUSIC] ✅ Процесс запущен, PID:', this.process.pid); this.isPlaying = true; this.isPaused = false; if (onStart) onStart(); this.process.on('close', (code) => { console.log('[MUSIC] Воспроизведение завершено:', trackPath, ', код:', code); if (this.isPlaying && this.repeatMode && this.currentTrack) { // Повторное воспроизведение console.log('[MUSIC] Повторное воспроизведение трека'); setTimeout(() => { if (this.repeatMode && this.currentTrack) { this._startPlayback(this.currentTrack, null, this.onTrackEnd); } }, 1000); } else { this.isPlaying = false; this.currentTrack = null; if (onEnd) onEnd(); } }); this.process.on('error', (error) => { console.error('[MUSIC] Ошибка процесса:', error.message); this.isPlaying = false; this.currentTrack = null; }); } // Установка громкости setVolume(volume) { this.volume = Math.max(0, Math.min(100, volume)); console.log('[MUSIC] Установлена громкость:', this.volume); // Если музыка играет, перезапускаем с новой громкостью if (this.isPlaying && this.currentTrack) { const currentTrack = this.currentTrack; this.stop(); this._startPlayback(currentTrack, null, this.onTrackEnd); } return this; } // Приглушение звука (во время баркера) duck() { if (!this.isDucked && this.isPlaying) { console.log('[MUSIC] Приглушаем музыку для баркера'); this.isDucked = true; if (this.currentTrack) { const currentTrack = this.currentTrack; this.stop(); this._startPlayback(currentTrack, null, this.onTrackEnd); } } return this; } // Восстановление звука (после баркера) unduck() { if (this.isDucked && this.isPlaying) { console.log('[MUSIC] Восстанавливаем громкость музыки'); this.isDucked = false; if (this.currentTrack) { const currentTrack = this.currentTrack; this.stop(); this._startPlayback(currentTrack, null, this.onTrackEnd); } } return this; } // Включение/отключение повтора setRepeat(repeat) { console.log('[MUSIC] setRepeat вызван с параметром:', repeat, 'тип:', typeof repeat); console.log('[MUSIC] Текущий режим повтора до изменения:', this.repeatMode); this.repeatMode = Boolean(repeat); console.log('[MUSIC] Режим повтора установлен на:', this.repeatMode ? 'включен' : 'отключен'); return this; } // Получение состояния плеера getStatus() { return { isPlaying: this.isPlaying, isPaused: this.isPaused, currentTrack: this.currentTrack ? path.basename(this.currentTrack) : null, volume: this.volume, repeatMode: this.repeatMode, isDucked: this.isDucked }; } } module.exports = MusicPlayer;