- Клиентская часть Vue 3 + Vite - Серверная часть Node.js + WebSocket - Система авторизации и смен - Управление игровыми портами - Поддержка тем (светлая/темная) - Адаптивный дизайн 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
162 lines
6.2 KiB
JavaScript
162 lines
6.2 KiB
JavaScript
// Браузерное радио - открывает радио в скрытом браузере
|
||
const { spawn, exec } = require('child_process');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const http = require('http');
|
||
|
||
class BrowserRadio {
|
||
constructor() {
|
||
this.server = null;
|
||
this.browserProcess = null;
|
||
this.isPlaying = false;
|
||
this.volume = 70;
|
||
this.currentUrl = null;
|
||
this.port = 8888;
|
||
}
|
||
|
||
play(url) {
|
||
this.stop();
|
||
|
||
// Добавляем протокол если его нет
|
||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||
url = 'http://' + url;
|
||
}
|
||
|
||
console.log('[RADIO] 📻 Запуск радио через браузер:', url);
|
||
this.currentUrl = url;
|
||
|
||
// Создаем HTML страницу с плеером
|
||
const html = `
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>Radio Player</title>
|
||
<style>
|
||
body { background: #000; color: #fff; font-family: Arial; padding: 20px; }
|
||
audio { width: 100%; margin: 20px 0; }
|
||
.controls { margin: 20px 0; }
|
||
button { padding: 10px 20px; margin: 5px; }
|
||
.status { margin: 10px 0; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h2>🎵 Радио плеер</h2>
|
||
<audio id="player" controls autoplay>
|
||
<source src="${url}" type="audio/mpeg">
|
||
<source src="${url}" type="audio/aac">
|
||
<source src="${url}" type="audio/ogg">
|
||
</audio>
|
||
<div class="controls">
|
||
<button onclick="setVolume(0)">0%</button>
|
||
<button onclick="setVolume(25)">25%</button>
|
||
<button onclick="setVolume(50)">50%</button>
|
||
<button onclick="setVolume(75)">75%</button>
|
||
<button onclick="setVolume(100)">100%</button>
|
||
</div>
|
||
<div class="status">
|
||
<p>Станция: ${url}</p>
|
||
<p>Громкость: <span id="volume">${this.volume}</span>%</p>
|
||
<p>Статус: <span id="status">Загрузка...</span></p>
|
||
</div>
|
||
<script>
|
||
const player = document.getElementById('player');
|
||
const volumeSpan = document.getElementById('volume');
|
||
const statusSpan = document.getElementById('status');
|
||
|
||
// Установка начальной громкости
|
||
player.volume = ${this.volume / 100};
|
||
|
||
// Обработчики событий
|
||
player.onloadstart = () => statusSpan.textContent = 'Подключение...';
|
||
player.oncanplay = () => statusSpan.textContent = 'Готово к воспроизведению';
|
||
player.onplay = () => statusSpan.textContent = 'Играет';
|
||
player.onpause = () => statusSpan.textContent = 'Пауза';
|
||
player.onerror = () => statusSpan.textContent = 'Ошибка загрузки';
|
||
|
||
// Функция установки громкости
|
||
function setVolume(percent) {
|
||
player.volume = percent / 100;
|
||
volumeSpan.textContent = percent;
|
||
// Отправляем на сервер
|
||
fetch('/volume/' + percent);
|
||
}
|
||
|
||
// Автообновление громкости с сервера
|
||
setInterval(() => {
|
||
fetch('/status')
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.volume !== undefined) {
|
||
player.volume = data.volume / 100;
|
||
volumeSpan.textContent = data.volume;
|
||
}
|
||
});
|
||
}, 1000);
|
||
</script>
|
||
</body>
|
||
</html>`;
|
||
|
||
// Создаем временный HTTP сервер
|
||
this.server = http.createServer((req, res) => {
|
||
if (req.url === '/') {
|
||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||
res.end(html);
|
||
} else if (req.url === '/status') {
|
||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||
res.end(JSON.stringify({ volume: this.volume, isPlaying: this.isPlaying }));
|
||
} else if (req.url.startsWith('/volume/')) {
|
||
const vol = parseInt(req.url.split('/')[2]);
|
||
if (!isNaN(vol)) {
|
||
this.volume = Math.max(0, Math.min(100, vol));
|
||
console.log('[RADIO] 🔊 Громкость установлена через браузер:', this.volume);
|
||
}
|
||
res.writeHead(200);
|
||
res.end('OK');
|
||
} else {
|
||
res.writeHead(404);
|
||
res.end('Not Found');
|
||
}
|
||
});
|
||
|
||
// Запускаем сервер
|
||
this.server.listen(this.port, () => {
|
||
console.log(`[RADIO] HTTP сервер запущен на порту ${this.port}`);
|
||
|
||
// Открываем в браузере
|
||
const browserUrl = `http://localhost:${this.port}`;
|
||
console.log('[RADIO] Открываем браузер:', browserUrl);
|
||
|
||
// Попробуем открыть в минимизированном окне
|
||
exec(`start /min ${browserUrl}`, (err) => {
|
||
if (err) {
|
||
console.log('[RADIO] Открываем в обычном режиме');
|
||
exec(`start ${browserUrl}`);
|
||
}
|
||
});
|
||
|
||
this.isPlaying = true;
|
||
});
|
||
}
|
||
|
||
stop() {
|
||
if (this.server) {
|
||
console.log('[RADIO] ⏹️ Остановка радио сервера');
|
||
this.server.close();
|
||
this.server = null;
|
||
}
|
||
|
||
// Закрываем браузер
|
||
exec('taskkill /F /FI "WINDOWTITLE eq Radio Player*"', { stdio: 'ignore' });
|
||
|
||
this.isPlaying = false;
|
||
this.currentUrl = null;
|
||
}
|
||
|
||
setVolume(volume) {
|
||
this.volume = Math.max(0, Math.min(100, volume));
|
||
console.log('[RADIO] 🔊 Установка громкости:', this.volume);
|
||
// Громкость обновится автоматически через HTTP запрос от браузера
|
||
}
|
||
}
|
||
|
||
module.exports = BrowserRadio; |