- Клиентская часть Vue 3 + Vite - Серверная часть Node.js + WebSocket - Система авторизации и смен - Управление игровыми портами - Поддержка тем (светлая/темная) - Адаптивный дизайн 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
257 lines
10 KiB
JavaScript
257 lines
10 KiB
JavaScript
// Улучшенный музыкальный плеер с поддержкой позиции воспроизведения
|
||
const { spawn } = require('child_process');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
class EnhancedMusicPlayer {
|
||
constructor() {
|
||
this.currentProcess = null;
|
||
this.currentTrack = null;
|
||
this.currentTrackIndex = -1;
|
||
this.isPlaying = false;
|
||
this.isPaused = false;
|
||
this.pausedPosition = 0; // Позиция в секундах где была пауза
|
||
this.startTime = null; // Время начала воспроизведения
|
||
this.volume = 70;
|
||
this.playlist = [];
|
||
|
||
// Для Windows - использование VLC или MPV для точной позиции
|
||
this.playerCommand = this._detectPlayer();
|
||
|
||
console.log('[ENHANCED-MUSIC] 🎵 Музыкальный плеер инициализирован');
|
||
console.log('[ENHANCED-MUSIC] 🎮 Используемый плеер:', this.playerCommand);
|
||
}
|
||
|
||
// Определение доступного плеера
|
||
_detectPlayer() {
|
||
if (process.platform === 'win32') {
|
||
// Проверяем наличие VLC
|
||
const vlcPath = 'C:\\Program Files\\VideoLAN\\VLC\\vlc.exe';
|
||
const vlcPath64 = 'C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe';
|
||
|
||
if (fs.existsSync(vlcPath) || fs.existsSync(vlcPath64)) {
|
||
return fs.existsSync(vlcPath) ? vlcPath : vlcPath64;
|
||
}
|
||
|
||
// Проверяем MPV
|
||
const mpvPath = path.join(__dirname, 'tools', 'mpv.exe');
|
||
if (fs.existsSync(mpvPath)) {
|
||
return mpvPath;
|
||
}
|
||
|
||
// Fallback на PowerShell
|
||
return 'powershell';
|
||
}
|
||
|
||
// Для Linux - mpv, vlc или mplayer
|
||
return 'mpv'; // или 'vlc', 'mplayer'
|
||
}
|
||
|
||
// Остановка воспроизведения
|
||
stop() {
|
||
console.log('[ENHANCED-MUSIC] ⏹️ Остановка воспроизведения');
|
||
|
||
if (this.currentProcess) {
|
||
try {
|
||
if (this.playerCommand.includes('vlc')) {
|
||
// VLC команда остановки
|
||
this.currentProcess.stdin.write('quit\n');
|
||
} else if (this.playerCommand.includes('mpv')) {
|
||
// MPV команда остановки
|
||
this.currentProcess.stdin.write('q');
|
||
} else {
|
||
// Обычное завершение процесса
|
||
this.currentProcess.kill();
|
||
}
|
||
} catch (err) {
|
||
console.log('[ENHANCED-MUSIC] ⚠️ Ошибка при остановке:', err.message);
|
||
}
|
||
this.currentProcess = null;
|
||
}
|
||
|
||
this.isPlaying = false;
|
||
this.isPaused = false;
|
||
this.pausedPosition = 0;
|
||
this.startTime = null;
|
||
this.currentTrack = null;
|
||
|
||
return this;
|
||
}
|
||
|
||
// Воспроизведение трека с позиции
|
||
play(trackPath, startPosition = 0) {
|
||
console.log('[ENHANCED-MUSIC] 🎵 Запуск воспроизведения:', trackPath, 'с позиции:', startPosition);
|
||
|
||
// Останавливаем текущее воспроизведение
|
||
this.stop();
|
||
|
||
if (!fs.existsSync(trackPath)) {
|
||
console.error('[ENHANCED-MUSIC] ❌ Файл не найден:', trackPath);
|
||
return this;
|
||
}
|
||
|
||
this.currentTrack = trackPath;
|
||
this.startTime = Date.now() - (startPosition * 1000);
|
||
|
||
let command, args;
|
||
|
||
if (this.playerCommand.includes('vlc')) {
|
||
// VLC с позицией воспроизведения
|
||
command = this.playerCommand;
|
||
args = [
|
||
'--intf', 'dummy',
|
||
'--play-and-exit',
|
||
'--no-video',
|
||
'--start-time', startPosition.toString(),
|
||
'--volume', Math.floor(this.volume * 2.56).toString(),
|
||
trackPath
|
||
];
|
||
} else if (this.playerCommand.includes('mpv')) {
|
||
// MPV с позицией воспроизведения
|
||
command = this.playerCommand;
|
||
args = [
|
||
'--no-video',
|
||
'--really-quiet',
|
||
'--start=' + startPosition,
|
||
'--volume=' + this.volume,
|
||
'--input-ipc-server=\\\\.\\pipe\\mpv-socket',
|
||
trackPath
|
||
];
|
||
} else {
|
||
// PowerShell fallback (без поддержки позиции)
|
||
console.log('[ENHANCED-MUSIC] ⚠️ PowerShell не поддерживает возобновление с позиции');
|
||
command = 'powershell';
|
||
args = ['-WindowStyle', 'Hidden', '-Command', `
|
||
Add-Type -AssemblyName presentationCore
|
||
$player = New-Object System.Windows.Media.MediaPlayer
|
||
$player.Open([Uri]"${trackPath}")
|
||
$player.Volume = ${this.volume / 100}
|
||
Start-Sleep -Milliseconds 500
|
||
$player.Play()
|
||
while ($player.Position -lt $player.NaturalDuration.TimeSpan) {
|
||
Start-Sleep -Milliseconds 100
|
||
}
|
||
$player.Close()
|
||
`];
|
||
}
|
||
|
||
// Запускаем процесс
|
||
this.currentProcess = spawn(command, args, {
|
||
stdio: ['pipe', 'pipe', 'pipe'],
|
||
windowsHide: true
|
||
});
|
||
|
||
this.isPlaying = true;
|
||
this.isPaused = false;
|
||
|
||
this.currentProcess.on('close', (code) => {
|
||
console.log('[ENHANCED-MUSIC] ⏹️ Воспроизведение завершено');
|
||
this.isPlaying = false;
|
||
this.currentProcess = null;
|
||
});
|
||
|
||
this.currentProcess.on('error', (error) => {
|
||
console.error('[ENHANCED-MUSIC] ❌ Ошибка процесса:', error.message);
|
||
this.isPlaying = false;
|
||
this.currentProcess = null;
|
||
});
|
||
|
||
return this;
|
||
}
|
||
|
||
// Пауза с сохранением позиции
|
||
pause() {
|
||
if (!this.isPlaying || this.isPaused) return this;
|
||
|
||
console.log('[ENHANCED-MUSIC] ⏸️ Пауза');
|
||
|
||
// Вычисляем текущую позицию
|
||
const elapsedTime = (Date.now() - this.startTime) / 1000;
|
||
this.pausedPosition = elapsedTime;
|
||
|
||
console.log('[ENHANCED-MUSIC] 💾 Сохранена позиция:', this.pausedPosition, 'сек');
|
||
|
||
// Останавливаем процесс
|
||
if (this.currentProcess) {
|
||
if (this.playerCommand.includes('vlc') || this.playerCommand.includes('mpv')) {
|
||
// Для VLC/MPV отправляем команду паузы
|
||
this.currentProcess.stdin.write('p');
|
||
} else {
|
||
// Для других - останавливаем процесс
|
||
this.currentProcess.kill();
|
||
this.currentProcess = null;
|
||
}
|
||
}
|
||
|
||
this.isPaused = true;
|
||
this.isPlaying = false;
|
||
|
||
return this;
|
||
}
|
||
|
||
// Возобновление с сохраненной позиции
|
||
resume() {
|
||
if (!this.isPaused || !this.currentTrack) return this;
|
||
|
||
console.log('[ENHANCED-MUSIC] ▶️ Возобновление с позиции:', this.pausedPosition, 'сек');
|
||
|
||
if (this.currentProcess && (this.playerCommand.includes('vlc') || this.playerCommand.includes('mpv'))) {
|
||
// Для VLC/MPV просто снимаем с паузы
|
||
this.currentProcess.stdin.write('p');
|
||
this.isPaused = false;
|
||
this.isPlaying = true;
|
||
} else {
|
||
// Для других - запускаем заново с позиции
|
||
const track = this.currentTrack;
|
||
const position = this.pausedPosition;
|
||
this.play(track, position);
|
||
}
|
||
|
||
return this;
|
||
}
|
||
|
||
// Установка громкости
|
||
setVolume(volume) {
|
||
this.volume = Math.max(0, Math.min(100, volume));
|
||
console.log('[ENHANCED-MUSIC] 🔊 Установлена громкость:', this.volume);
|
||
|
||
if (this.isPlaying && this.currentProcess) {
|
||
if (this.playerCommand.includes('vlc')) {
|
||
// VLC команда громкости (0-256)
|
||
const vlcVolume = Math.floor(this.volume * 2.56);
|
||
this.currentProcess.stdin.write(`volume ${vlcVolume}\n`);
|
||
} else if (this.playerCommand.includes('mpv')) {
|
||
// MPV команда громкости через JSON IPC
|
||
const mpvCommand = JSON.stringify({
|
||
command: ["set_property", "volume", this.volume]
|
||
});
|
||
this.currentProcess.stdin.write(mpvCommand + '\n');
|
||
}
|
||
}
|
||
|
||
return this;
|
||
}
|
||
|
||
// Получение информации о плеере
|
||
getPlayerInfo() {
|
||
if (this.playerCommand.includes('vlc')) {
|
||
return {
|
||
name: 'VLC Media Player',
|
||
features: ['позиция воспроизведения', 'управление громкостью', 'пауза без остановки']
|
||
};
|
||
} else if (this.playerCommand.includes('mpv')) {
|
||
return {
|
||
name: 'MPV Player',
|
||
features: ['позиция воспроизведения', 'управление громкостью', 'пауза без остановки']
|
||
};
|
||
} else {
|
||
return {
|
||
name: 'Windows Media (PowerShell)',
|
||
features: ['базовое воспроизведение'],
|
||
limitations: ['нет сохранения позиции', 'системная громкость']
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = EnhancedMusicPlayer; |