您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Радио на 4PDA с поиском через API и флагами стран
- // ==UserScript==
- // @name 4PDA Radio v1.14
- // @author brant34
- // @namespace http://tampermonkey.net/
- // @version 1.14
- // @description Радио на 4PDA с поиском через API и флагами стран
- // @match https://4pda.to/forum/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
- (function () {
- 'use strict';
- // Проверка поддержки localStorage
- if (!window.localStorage) {
- showNotification('localStorage недоступен', 'error');
- return;
- }
- // === [Стили] ===
- GM_addStyle(`
- .radio-toggle-button {
- position: fixed;
- top: 10px;
- right: 10px;
- background-color: #2e6d5e;
- color: #fff;
- border: none;
- border-radius: 50%;
- width: 32px;
- height: 32px;
- cursor: pointer;
- font-size: 16px;
- line-height: 32px;
- text-align: center;
- z-index: 99999;
- }
- .radio-toggle-button:hover {
- background-color: #3e8e77;
- }
- .radio-panel {
- display: none;
- background-color: #1a3c34;
- border-radius: 10px;
- padding: 10px;
- z-index: 99998;
- color: #fff;
- font-family: Arial, sans-serif;
- font-size: 14px;
- width: 320px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
- position: fixed;
- max-width: 90vw;
- }
- .radio-panel-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 10px;
- }
- .radio-panel-header span {
- font-weight: bold;
- }
- .radio-panel-controls {
- display: flex;
- flex-wrap: wrap;
- gap: 5px;
- }
- .radio-panel-controls button {
- background-color: #17a2b8;
- color: #fff;
- border: none;
- border-radius: 5px;
- padding: 5px 10px;
- cursor: pointer;
- font-size: 12px;
- }
- .radio-panel-controls button:hover {
- background-color: #138496;
- }
- .radio-panel select {
- background-color: #fff;
- border: 1px solid #ccc;
- border-radius: 5px;
- padding: 5px;
- color: #333;
- font-size: 12px;
- flex-grow: 1;
- }
- .radio-player {
- background-color: transparent;
- border-radius: 5px;
- padding: 5px;
- margin: 10px 0;
- display: flex;
- align-items: center;
- gap: 5px;
- }
- .radio-player button {
- background: none;
- border: none;
- cursor: pointer;
- font-size: 16px;
- color: #fff;
- }
- .radio-player input[type="range"] {
- -webkit-appearance: none;
- appearance: none;
- background: transparent;
- cursor: pointer;
- width: 100%;
- }
- .radio-player input[type="range"]::-webkit-slider-runnable-track {
- background: #2e6d5e;
- height: 6px;
- border-radius: 3px;
- }
- .radio-player input[type="range"]::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- background: #fff;
- height: 16px;
- width: 16px;
- border-radius: 50%;
- margin-top: -5px;
- }
- .radio-player input[type="range"]::-moz-range-track {
- background: #2e6d5e;
- height: 6px;
- border-radius: 3px;
- }
- .radio-player input[type="range"]::-moz-range-thumb {
- background: #fff;
- height: 16px;
- width: 16px;
- border-radius: 50%;
- border: none;
- }
- .radio-player input[type="range"]::-moz-range-progress {
- background: #fff;
- height: 6px;
- border-radius: 3px;
- }
- .radio-player input[type="range"]:disabled::-webkit-slider-runnable-track {
- background: #1a3c34;
- opacity: 0.7;
- }
- .radio-player input[type="range"]:disabled::-webkit-slider-thumb {
- background: #999;
- }
- .radio-player input[type="range"]:disabled::-moz-range-track {
- background: #1a3c34;
- opacity: 0.7;
- }
- .radio-player input[type="range"]:disabled::-moz-range-thumb {
- background: #999;
- }
- .radio-player input[type="range"]:disabled::-moz-range-progress {
- background: #999;
- }
- .radio-player .volume-icon {
- cursor: pointer;
- }
- .radio-player .volume-icon.muted::before {
- content: "🔇";
- }
- .radio-player .volume-icon:not(.muted)::before {
- content: "🔊";
- }
- .radio-panel input[type="checkbox"] {
- margin-right: 5px;
- }
- .radio-panel-settings {
- margin-top: 10px;
- display: flex;
- gap: 5px;
- }
- .radio-search {
- display: flex;
- gap: 5px;
- margin: 10px 0;
- }
- .radio-search input[type="text"] {
- flex-grow: 1;
- padding: 5px;
- border-radius: 5px;
- border: 1px solid #ccc;
- background-color: #fff;
- color: #333;
- font-size: 12px;
- }
- .radio-search button {
- background-color: #17a2b8;
- color: #fff;
- border: none;
- border-radius: 5px;
- padding: 5px 10px;
- cursor: pointer;
- font-size: 12px;
- }
- .radio-search button:hover {
- background-color: #138496;
- }
- .radio-search-results {
- max-height: 150px;
- overflow-y: auto;
- margin-top: 5px;
- padding: 5px;
- background-color: #2e6d5e;
- border-radius: 5px;
- }
- .radio-search-results div {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 5px;
- border-bottom: 1px solid #1a3c34;
- }
- .radio-search-results div:last-child {
- border-bottom: none;
- }
- .radio-search-results button {
- background-color: #17a2b8;
- color: #fff;
- border: none;
- border-radius: 5px;
- padding: 3px 8px;
- cursor: pointer;
- font-size: 10px;
- }
- .radio-search-results button:hover {
- background-color: #138496;
- }
- .notification {
- position: fixed;
- top: 50px;
- right: 10px;
- padding: 10px 20px;
- border-radius: 5px;
- color: white;
- z-index: 99999;
- }
- .notification.success { background-color: #28a745; }
- .notification.info { background-color: #17a2b8; }
- .notification.warning { background-color: #ffc107; }
- .notification.error { background-color: #dc3545; }
- `);
- // === [Эксклюзивное воспроизведение радио во вкладке] ===
- const tabId = Date.now().toString();
- const MASTER_KEY = '4pda-radio-master';
- function setAsMaster() {
- localStorage.setItem(MASTER_KEY, tabId);
- showNotification('Эта вкладка теперь воспроизводит радио', 'success');
- }
- function isMaster() {
- return localStorage.getItem(MASTER_KEY) === tabId;
- }
- // === [Инициализация аудиоплеера] ===
- let audio = document.getElementById('radioPlayer4PDA');
- if (!audio) {
- audio = document.createElement('audio');
- audio.id = 'radioPlayer4PDA';
- document.body.appendChild(audio);
- }
- // Проверка перед запуском радио
- const currentMaster = localStorage.getItem(MASTER_KEY);
- if (!currentMaster) {
- setAsMaster();
- } else if (!isMaster()) {
- audio.pause();
- }
- // Слушаем изменения мастера
- window.addEventListener('storage', (e) => {
- if (e.key === MASTER_KEY && e.newValue !== tabId) {
- audio.pause();
- }
- });
- // Убираем себя из мастеров при закрытии вкладки
- window.addEventListener('beforeunload', () => {
- if (isMaster()) {
- localStorage.removeItem(MASTER_KEY);
- }
- });
- // === [Сохраненные настройки] ===
- const savedAutoplay = GM_getValue('autoplay', false);
- const savedRadio = GM_getValue('radio', '');
- const savedVolume = GM_getValue('volume', 1);
- const savedTimer = GM_getValue('autotimer', 0);
- const savedPlaying = GM_getValue('isPlaying', false);
- const savedTime = GM_getValue('currentTime', 0);
- let panelPosition = GM_getValue('panelPos', 'top-right');
- const panelScale = GM_getValue('panelSize', '1');
- const savedCustomStations = GM_getValue('customStations', {});
- // === [Список радиостанций] ===
- let RADIO = {
- '🇷🇺 Европа Плюс': 'https://ep256.hostingradio.ru:8052/europaplus256.mp3',
- '🇷🇺 Русское Радио': 'https://rusradio.hostingradio.ru/rusradio128.mp3',
- '🇷🇺 Юмор FM': 'https://pub0301.101.ru:8443/stream/air/mp3/256/102',
- '🇷🇺 Радио Рекорд': 'https://radio-srv1.11one.ru/record192k.mp3',
- '🇷🇺 Ретро FM': 'https://retro.hostingradio.ru:8014/retro320.mp3',
- '🇷🇺 Радио Шансон': 'https://chanson.hostingradio.ru:8041/chanson256.mp3',
- '🇷🇺 DFM Russian Dance': 'https://stream03.pcradio.ru/dfm_russian_dance-hi',
- '🇷🇺 DFM': 'https://dfm.hostingradio.ru:80/dfm96.aacp',
- '🇷🇺 Дорожное Радио': 'https://dorognoe.hostingradio.ru:8000/dorognoe',
- '🇷🇺 Авторадио': 'https://srv01.gpmradio.ru/stream/air/aac/64/100?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiIwZWM3MjU3YTFhNDM5MmMyNWUwZDZkZDQwYjdjNzQ5ZCIsIklQIjoiODEuMTczLjE2NS4yMjUiLCJVQSI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwXzE1XzcpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMzMuMC4wLjAgU2FmYXJpLzUzNy4zNiIsIlJlZiI6Imh0dHBzOi8vd3d3LmF2dG9yYWRpby5ydS8iLCJ1aWRfY2hhbm5lbCI6IjEwMCIsInR5cGVfY2hhbm5lbCI6ImNoYW5uZWwiLCJ0eXBlRGV2aWNlIjoiUEMiLCJCcm93c2VyIjoiQ2hyb21lIiwiQnJvd3NlclZlcnNpb24iOiIxMzMuMC4wLjAiLCJTeXN0ZW0iOiJNYWMgT1MgWCBQdW1hIiwiZXhwIjoxNzQyNjcxOTc1fQ.b1Hha0aGp4hWbgFELSzEapRcpOoejzs8tmdDARY0JyA',
- '🇩🇪 Радио Картина': 'https://rs.kartina.tv/kartina_320kb',
- '🇰🇿 LuxFM': 'https://icecast.luxfm.kz/luxfm',
- '🇰🇿 Radio NS': 'https://icecast.ns.kz/radions',
- '🇰🇿 NRJ Kazakhstan': 'https://stream03.pcradio.ru/energyfm_ru-med',
- '🇰🇿 Радио Жаңа FM': 'https://live.zhanafm.kz:8443/zhanafm_onair',
- '🇺🇦 Хіт FM': 'http://online.hitfm.ua/HitFM',
- '🇺🇦 Kiss FM UA': 'http://online.kissfm.ua/KissFM'
- };
- // Объединяем предопределенные станции с пользовательскими
- Object.assign(RADIO, savedCustomStations);
- // === [Проверка доступности радиопотоков] ===
- async function checkStream(url) {
- return true; // Заглушка, можно добавить реальную проверку
- }
- async function validateStations() {
- const validStations = {};
- for (const [name, url] of Object.entries(RADIO)) {
- if (await checkStream(url)) {
- validStations[name] = url;
- } else {
- showNotification(`Радиостанция ${name} недоступна`, 'warning');
- }
- }
- RADIO = validStations;
- updateStationList();
- }
- // === [Динамическое обновление списка радиостанций] ===
- async function loadStations() {
- try {
- const response = await new Promise((resolve) => {
- setTimeout(() => resolve({ ok: true, json: () => Promise.resolve(RADIO) }), 1000);
- });
- if (response.ok) {
- RADIO = await response.json();
- await validateStations();
- showNotification('Список радиостанций обновлен', 'success');
- } else {
- showNotification('Ошибка загрузки списка радиостанций', 'error');
- }
- } catch (error) {
- console.error('Ошибка обновления радиостанций:', error);
- showNotification('Ошибка обновления радиостанций', 'error');
- }
- }
- // === [Уведомления] ===
- function showNotification(message, type) {
- const notification = document.createElement('div');
- notification.className = `notification ${type}`;
- notification.textContent = message;
- document.body.appendChild(notification);
- setTimeout(() => notification.remove(), 3000);
- }
- // === [Преобразование кода страны в флаг] ===
- function countryCodeToFlag(countryCode) {
- if (!countryCode || countryCode.length !== 2) {
- return '🌐'; // Нейтральный флаг, если код страны отсутствует
- }
- const codePoints = countryCode
- .toUpperCase()
- .split('')
- .map(char => 0x1F1E6 + (char.charCodeAt(0) - 65)); // 'A' → 0x1F1E6, 'B' → 0x1F1E7, ..., 'U' → 0x1F1FA
- return String.fromCodePoint(...codePoints);
- }
- // === [Поиск через RadioBrowser API] ===
- function searchStations(query, callback) {
- showNotification('Поиск...', 'info');
- GM_xmlhttpRequest({
- method: 'GET',
- url: `https://de1.api.radio-browser.info/json/stations/search?name=${encodeURIComponent(query)}&limit=10`,
- onload: function(response) {
- try {
- const data = JSON.parse(response.responseText);
- const results = data.map(station => ({
- name: station.name,
- url: station.url_resolved,
- countryCode: station.countrycode || ''
- }));
- callback(results);
- } catch (error) {
- console.error('Ошибка парсинга ответа API:', error);
- showNotification('Ошибка поиска радиостанций', 'error');
- callback([]);
- }
- },
- onerror: function(error) {
- console.error('Ошибка запроса к API:', error);
- showNotification('Ошибка поиска радиостанций', 'error');
- callback([]);
- }
- });
- }
- function addStation(name, url, countryCode) {
- // Добавляем флаг к имени станции
- const flag = countryCodeToFlag(countryCode);
- const stationNameWithFlag = `${flag} ${name}`;
- if (RADIO[stationNameWithFlag]) {
- showNotification('Радиостанция уже добавлена', 'warning');
- return;
- }
- RADIO[stationNameWithFlag] = url;
- const customStations = GM_getValue('customStations', {});
- customStations[stationNameWithFlag] = url;
- GM_setValue('customStations', customStations);
- updateStationList();
- showNotification(`Радиостанция ${stationNameWithFlag} добавлена`, 'success');
- }
- // === [Интерфейс] ===
- function createInterface() {
- // Панель радио
- const panel = document.createElement('div');
- panel.className = 'radio-panel';
- panel.style.display = GM_getValue('panelVisible', false) ? 'block' : 'none';
- // Кнопка S
- const toggleButton = document.createElement('button');
- toggleButton.className = 'radio-toggle-button';
- toggleButton.textContent = '🎧';
- toggleButton.onclick = () => {
- if (!panel) {
- console.error('Панель не найдена');
- showNotification('Ошибка: панель не создана', 'error');
- return;
- }
- panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
- GM_setValue('panelVisible', panel.style.display === 'block');
- showNotification(`Панель ${panel.style.display === 'block' ? 'открыта' : 'закрыта'}`, 'info');
- };
- document.body.appendChild(toggleButton);
- // Применение позиции и масштаба
- updatePanelPosition(panel, panelPosition);
- updatePanelScale(panel, panelScale);
- // Заголовок
- const header = document.createElement('div');
- header.className = 'radio-panel-header';
- header.innerHTML = '<span>⚡ Громкость:</span>';
- panel.appendChild(header);
- // Кнопки пресетов громкости
- const controls = document.createElement('div');
- controls.className = 'radio-panel-controls';
- ['Тихо', 'Комфорт', 'Громко'].forEach((label, index) => {
- const button = document.createElement('button');
- button.textContent = label;
- button.onclick = () => {
- if (!audio) {
- console.error('Аудиоплеер не инициализирован');
- showNotification('Ошибка: аудиоплеер не доступен', 'error');
- return;
- }
- const volumes = [0.2, 0.5, 0.8];
- audio.volume = volumes[index];
- GM_setValue('volume', audio.volume);
- updateVolumeSlider(audio.volume);
- showNotification(`Громкость: ${label} (${volumes[index] * 100}%)`, 'info');
- };
- controls.appendChild(button);
- });
- panel.appendChild(controls);
- // Ползунок громкости
- const volumeSlider = document.createElement('input');
- volumeSlider.type = 'range';
- volumeSlider.min = '0';
- volumeSlider.max = '1';
- volumeSlider.step = '0.01';
- volumeSlider.value = savedVolume;
- volumeSlider.oninput = () => {
- if (!audio) {
- console.error('Аудиоплеер не инициализирован');
- showNotification('Ошибка: аудиоплеер не доступен', 'error');
- return;
- }
- audio.volume = volumeSlider.value;
- GM_setValue('volume', audio.volume);
- };
- controls.appendChild(volumeSlider);
- // Выбор радиостанции
- const stationSelect = document.createElement('select');
- stationSelect.id = 'radioStationSelect';
- updateStationList();
- controls.appendChild(stationSelect);
- panel.appendChild(controls);
- // Поиск радиостанций
- const searchSection = document.createElement('div');
- searchSection.className = 'radio-search';
- const searchInput = document.createElement('input');
- searchInput.type = 'text';
- searchInput.placeholder = 'Поиск радиостанций...';
- searchSection.appendChild(searchInput);
- const searchButton = document.createElement('button');
- searchButton.textContent = 'Поиск';
- searchSection.appendChild(searchButton);
- panel.appendChild(searchSection);
- // Результаты поиска
- const searchResults = document.createElement('div');
- searchResults.className = 'radio-search-results';
- searchResults.style.display = 'none';
- panel.appendChild(searchResults);
- searchButton.onclick = () => {
- const query = searchInput.value.trim();
- if (!query) {
- showNotification('Введите запрос для поиска', 'warning');
- return;
- }
- searchStations(query, (results) => {
- searchResults.innerHTML = '';
- searchResults.style.display = results.length ? 'block' : 'none';
- if (!results.length) {
- showNotification('Радиостанции не найдены', 'info');
- return;
- }
- results.forEach(station => {
- const resultItem = document.createElement('div');
- const flag = countryCodeToFlag(station.countryCode);
- resultItem.textContent = `${flag} ${station.name}`;
- const addButton = document.createElement('button');
- addButton.textContent = 'Добавить';
- addButton.onclick = () => {
- addStation(station.name, station.url, station.countryCode);
- searchResults.style.display = 'none';
- searchInput.value = '';
- };
- resultItem.appendChild(addButton);
- searchResults.appendChild(resultItem);
- });
- });
- };
- // Плеер
- const player = document.createElement('div');
- player.className = 'radio-player';
- const playButton = document.createElement('button');
- playButton.textContent = savedPlaying ? '⏸' : '▶';
- playButton.onclick = togglePlay;
- player.appendChild(playButton);
- const timeSlider = document.createElement('input');
- timeSlider.type = 'range';
- timeSlider.min = '0';
- timeSlider.max = '100';
- timeSlider.value = '0';
- timeSlider.disabled = true;
- player.appendChild(timeSlider);
- const timeDisplay = document.createElement('span');
- timeDisplay.textContent = '0:00';
- player.appendChild(timeDisplay);
- const volumeIcon = document.createElement('span');
- volumeIcon.className = 'volume-icon';
- volumeIcon.onclick = () => {
- if (!audio) {
- console.error('Аудиоплеер не инициализирован');
- showNotification('Ошибка: аудиоплеер не доступен', 'error');
- return;
- }
- audio.muted = !audio.muted;
- volumeIcon.classList.toggle('muted', audio.muted);
- showNotification(audio.muted ? 'Звук выключен' : 'Звук включен', 'info');
- };
- player.appendChild(volumeIcon);
- panel.appendChild(player);
- // Таймер, автостарт и обновление
- const footer = document.createElement('div');
- footer.className = 'radio-panel-controls';
- const timerSelect = document.createElement('select');
- timerSelect.innerHTML = `
- <option value="0">Без таймера</option>
- <option value="10">10 мин</option>
- <option value="30">30 мин</option>
- <option value="60">60 мин</option>
- `;
- timerSelect.value = savedTimer;
- timerSelect.onchange = () => {
- GM_setValue('autotimer', parseInt(timerSelect.value) || 0);
- setAutoTimer(parseInt(timerSelect.value) || 0);
- };
- footer.appendChild(timerSelect);
- const autostartLabel = document.createElement('label');
- const autostartCheckbox = document.createElement('input');
- autostartCheckbox.type = 'checkbox';
- autostartCheckbox.checked = savedAutoplay;
- autostartCheckbox.onchange = () => {
- GM_setValue('autoplay', autostartCheckbox.checked);
- };
- autostartLabel.appendChild(autostartCheckbox);
- autostartLabel.appendChild(document.createTextNode('Автостарт'));
- footer.appendChild(autostartLabel);
- const refreshButton = document.createElement('button');
- refreshButton.textContent = '↻';
- refreshButton.title = 'Обновить станции';
- refreshButton.onclick = loadStations;
- footer.appendChild(refreshButton);
- panel.appendChild(footer);
- // Настройки панели
- const settings = document.createElement('div');
- settings.className = 'radio-panel-settings';
- const positionSelect = document.createElement('select');
- positionSelect.innerHTML = `
- <option value="top-left">Вверху слева</option>
- <option value="top-center">Вверху посередине</option>
- <option value="top-right">Вверху справа</option>
- `;
- positionSelect.value = panelPosition;
- positionSelect.onchange = () => {
- GM_setValue('panelPos', positionSelect.value);
- panelPosition = positionSelect.value;
- updatePanelPosition(panel, positionSelect.value);
- showNotification(`Панель перемещена: ${positionSelect.options[positionSelect.selectedIndex].text}`, 'info');
- };
- settings.appendChild(positionSelect);
- const scaleSelect = document.createElement('select');
- scaleSelect.innerHTML = `
- <option value="0.8">Маленький</option>
- <option value="1">Средний</option>
- <option value="1.1">Большой</option>
- `;
- scaleSelect.value = panelScale;
- scaleSelect.onchange = () => {
- GM_setValue('panelSize', scaleSelect.value);
- updatePanelScale(panel, scaleSelect.value);
- showNotification(`Масштаб панели: ${scaleSelect.options[scaleSelect.selectedIndex].text}`, 'info');
- };
- settings.appendChild(scaleSelect);
- panel.appendChild(settings);
- document.body.appendChild(panel);
- // Функция для обновления ползунка громкости
- function updateVolumeSlider(value) {
- volumeSlider.value = value;
- }
- }
- function updatePanelPosition(panel, position) {
- if (!panel) {
- console.error('Панель не найдена для обновления позиции');
- return;
- }
- panel.style.top = '10px';
- panel.style.bottom = '';
- panel.style.left = '';
- panel.style.right = '';
- panel.style.transform = '';
- switch (position) {
- case 'top-left':
- panel.style.left = '10px';
- break;
- case 'top-center':
- panel.style.left = '50%';
- panel.style.transform = 'translateX(-50%)';
- break;
- case 'top-right':
- panel.style.right = '10px';
- break;
- }
- }
- function updatePanelScale(panel, scale) {
- if (!panel) {
- console.error('Панель не найдена для обновления масштаба');
- return;
- }
- panel.style.transform = `scale(${scale})`;
- panel.style.transformOrigin = panelPosition.includes('left') ? 'top left' : panelPosition.includes('right') ? 'top right' : 'top center';
- if (parseFloat(scale) > 1) {
- panel.style.maxWidth = '80vw';
- if (panelPosition === 'top-center') {
- panel.style.left = '50%';
- panel.style.transform = `translateX(-50%) scale(${scale})`;
- }
- } else {
- panel.style.maxWidth = '90vw';
- }
- }
- function updateStationList() {
- const stationSelect = document.getElementById('radioStationSelect');
- if (!stationSelect) {
- return;
- }
- stationSelect.innerHTML = '<option value="">Выберите радиостанцию</option>';
- Object.keys(RADIO).forEach(name => {
- const option = document.createElement('option');
- option.value = RADIO[name];
- option.textContent = name;
- if (RADIO[name] === savedRadio) option.selected = true;
- stationSelect.appendChild(option);
- });
- stationSelect.onchange = () => {
- if (stationSelect.value) {
- audio.src = stationSelect.value;
- GM_setValue('radio', stationSelect.value);
- if (savedAutoplay || savedPlaying) {
- audio.play().catch(e => {
- console.error('Ошибка воспроизведения:', e);
- showNotification('Ошибка воспроизведения радиостанции', 'error');
- });
- }
- }
- };
- }
- // === [Управление воспроизведением] ===
- function togglePlay() {
- const playButton = document.querySelector('.radio-player button');
- if (!playButton) {
- return;
- }
- if (audio.paused) {
- if (isMaster()) {
- audio.play().catch(e => {
- console.error('Ошибка воспроизведения:', e);
- showNotification('Ошибка воспроизведения радиостанции', 'error');
- });
- GM_setValue('isPlaying', true);
- playButton.textContent = '⏸';
- }
- } else {
- audio.pause();
- GM_setValue('isPlaying', false);
- playButton.textContent = '▶';
- }
- }
- // === [Таймер автовыключения] ===
- let timerId;
- function setAutoTimer(minutes) {
- clearTimeout(timerId);
- if (minutes > 0) {
- timerId = setTimeout(() => {
- audio.pause();
- GM_setValue('isPlaying', false);
- const playButton = document.querySelector('.radio-player button');
- if (playButton) {
- playButton.textContent = '▶';
- }
- showNotification('Радио остановлено по таймеру', 'info');
- }, minutes * 60 * 1000);
- }
- }
- // === [Инициализация] ===
- try {
- createInterface();
- // Инициализация аудио
- audio.volume = savedVolume;
- if (savedRadio) {
- audio.src = savedRadio;
- if (savedAutoplay && isMaster()) {
- audio.play().catch(e => {
- console.error('Ошибка воспроизведения:', e);
- showNotification('Ошибка воспроизведения радиостанции', 'error');
- });
- }
- }
- audio.ontimeupdate = () => GM_setValue('currentTime', audio.currentTime);
- audio.onerror = () => {
- console.error('Ошибка загрузки радиопотока');
- showNotification('Ошибка загрузки радиопотока', 'error');
- };
- // Обновление списка радиостанций без проверки
- updateStationList();
- setAutoTimer(savedTimer);
- } catch (error) {
- console.error('Критическая ошибка инициализации:', error);
- showNotification('Ошибка запуска скрипта', 'error');
- }
- })();