- Клиентская часть Vue 3 + Vite - Серверная часть Node.js + WebSocket - Система авторизации и смен - Управление игровыми портами - Поддержка тем (светлая/темная) - Адаптивный дизайн 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
177 lines
6.3 KiB
JavaScript
177 lines
6.3 KiB
JavaScript
// Специальный радио плеер для Linux с изолированным управлением громкостью
|
||
const { spawn } = require('child_process');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
class LinuxRadioPlayer {
|
||
constructor() {
|
||
this.radioProcess = null;
|
||
this.isPlaying = false;
|
||
this.volume = 70;
|
||
this.currentUrl = null;
|
||
this.playerType = null;
|
||
}
|
||
|
||
detectPlayer() {
|
||
// Определяем доступный плеер в порядке приоритета
|
||
const players = [
|
||
{ name: 'cvlc', check: 'vlc --version' },
|
||
{ name: 'mplayer', check: 'mplayer -version' },
|
||
{ name: 'mpg123', check: 'mpg123 --version' },
|
||
{ name: 'ffplay', check: 'ffplay -version' }
|
||
];
|
||
|
||
for (const player of players) {
|
||
try {
|
||
require('child_process').execSync(player.check, { stdio: 'ignore' });
|
||
console.log(`[LINUX-RADIO] ✅ Найден плеер: ${player.name}`);
|
||
return player.name;
|
||
} catch (e) {
|
||
// Плеер не найден
|
||
}
|
||
}
|
||
|
||
console.log('[LINUX-RADIO] ⚠️ Не найдены рекомендуемые плееры, используем paplay');
|
||
return 'paplay';
|
||
}
|
||
|
||
play(url) {
|
||
this.stop();
|
||
|
||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||
url = 'http://' + url;
|
||
}
|
||
|
||
console.log('[LINUX-RADIO] 📻 Запуск радио:', url);
|
||
this.currentUrl = url;
|
||
this.playerType = this.detectPlayer();
|
||
|
||
let command, args;
|
||
|
||
switch (this.playerType) {
|
||
case 'cvlc':
|
||
// VLC - лучший вариант, поддерживает программное управление громкостью
|
||
command = 'cvlc';
|
||
args = [
|
||
'--intf', 'dummy',
|
||
'--no-video',
|
||
'--no-video-title-show',
|
||
'--quiet',
|
||
'--volume', Math.floor(this.volume * 2.56).toString(), // VLC использует 0-256
|
||
'--no-volume-save', // Не сохранять громкость
|
||
url
|
||
];
|
||
break;
|
||
|
||
case 'mplayer':
|
||
// MPlayer - хороший вариант с программной громкостью
|
||
command = 'mplayer';
|
||
args = [
|
||
'-quiet',
|
||
'-really-quiet',
|
||
'-nolirc',
|
||
'-novideo',
|
||
'-volume', this.volume.toString(),
|
||
'-softvol',
|
||
'-softvol-max', '100',
|
||
url
|
||
];
|
||
break;
|
||
|
||
case 'mpg123':
|
||
// mpg123 - поддерживает масштабирование громкости
|
||
command = 'mpg123';
|
||
args = [
|
||
'--quiet',
|
||
'--scale', (this.volume / 100).toString(),
|
||
url
|
||
];
|
||
break;
|
||
|
||
case 'ffplay':
|
||
// FFplay - часть FFmpeg
|
||
command = 'ffplay';
|
||
args = [
|
||
'-nodisp',
|
||
'-autoexit',
|
||
'-loglevel', 'error',
|
||
'-volume', this.volume.toString(),
|
||
url
|
||
];
|
||
break;
|
||
|
||
default:
|
||
// Fallback на paplay с громкостью
|
||
const paVolume = Math.floor((this.volume / 100) * 65536);
|
||
command = 'sh';
|
||
args = ['-c', `curl -s "${url}" | paplay --raw --format=s16le --rate=44100 --channels=2 --volume=${paVolume}`];
|
||
}
|
||
|
||
console.log(`[LINUX-RADIO] Используем ${this.playerType}:`, command, args.join(' '));
|
||
|
||
this.radioProcess = spawn(command, args, {
|
||
stdio: 'ignore', // Игнорируем весь вывод
|
||
detached: false
|
||
});
|
||
|
||
this.radioProcess.on('error', (error) => {
|
||
console.error('[LINUX-RADIO] Ошибка запуска:', error.message);
|
||
this.isPlaying = false;
|
||
});
|
||
|
||
this.radioProcess.on('exit', (code) => {
|
||
console.log('[LINUX-RADIO] Процесс завершен с кодом:', code);
|
||
this.isPlaying = false;
|
||
this.radioProcess = null;
|
||
});
|
||
|
||
this.isPlaying = true;
|
||
}
|
||
|
||
stop() {
|
||
if (this.radioProcess) {
|
||
console.log('[LINUX-RADIO] ⏹️ Остановка радио');
|
||
|
||
try {
|
||
this.radioProcess.kill('SIGTERM');
|
||
setTimeout(() => {
|
||
if (this.radioProcess) {
|
||
this.radioProcess.kill('SIGKILL');
|
||
}
|
||
}, 1000);
|
||
} catch (e) {
|
||
console.error('[LINUX-RADIO] Ошибка остановки:', e.message);
|
||
}
|
||
|
||
this.radioProcess = null;
|
||
}
|
||
|
||
this.isPlaying = false;
|
||
this.currentUrl = null;
|
||
}
|
||
|
||
setVolume(volume) {
|
||
this.volume = Math.max(0, Math.min(100, volume));
|
||
console.log('[LINUX-RADIO] 🔊 Новая громкость:', this.volume);
|
||
|
||
// Для изменения громкости на лету нужно перезапустить поток
|
||
// Это не идеально, но гарантирует отсутствие влияния на системную громкость
|
||
if (this.isPlaying && this.currentUrl) {
|
||
console.log('[LINUX-RADIO] Перезапуск потока с новой громкостью...');
|
||
const url = this.currentUrl;
|
||
this.stop();
|
||
setTimeout(() => this.play(url), 100);
|
||
}
|
||
}
|
||
|
||
getStatus() {
|
||
return {
|
||
isPlaying: this.isPlaying,
|
||
volume: this.volume,
|
||
url: this.currentUrl,
|
||
player: this.playerType
|
||
};
|
||
}
|
||
}
|
||
|
||
module.exports = { LinuxRadioPlayer }; |