您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Дополнения для более комфортного пребывания на 4PDA
// ==UserScript== // @name 4PDA Fonts & Radio v.1.0 // @author brant34 // @namespace http://tampermonkey.net/ // @version 1.0-tfull // @description Дополнения для более комфортного пребывания на 4PDA // @match https://4pda.to/forum/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function () { 'use strict'; const savedSize = GM_getValue('size', '14px'); const savedFont = GM_getValue('font', 'verdana'); const savedAutoplay = GM_getValue('autoplay', false); const panelScale = GM_getValue('panelSize', '1'); const panelPosition = GM_getValue('panelPos', 'right'); 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); const FONTS = { 'verdana': 'Verdana','georgia': 'Georgia','open-sans': 'Open Sans','comfortaa': 'Comfortaa','nunito': 'Nunito', 'pt-sans': 'PT Sans','manrope': 'Manrope','rubik': 'Rubik','roboto': 'Roboto','ubuntu': 'Ubuntu','noto-sans': 'Noto Sans','montserrat': 'Montserrat' }; const 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' }; GM_addStyle(` @import url('https://fonts.googleapis.com/css2?family=Manrope&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Rubik&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Comfortaa&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); @import url('https://fonts.googleapis.com/css2?family=PT+Sans&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Ubuntu&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Noto+Sans&display=swap'); `); function applyStyles(font, size) { const selectors = ['body','.post','.msg','.signature','.post_wrap','.xbox','.code','.normalname','.desc','.maintitle','.postcolor','.nav','td','th']; selectors.forEach(selector => { document.querySelectorAll(selector).forEach(el => { el.style.setProperty('font-family', `'${FONTS[font]}', sans-serif`, 'important'); el.style.setProperty('font-size', size, 'important'); }); }); } function createPanelSettings(panel) { const gear = document.createElement('span'); gear.textContent = '⚙️'; gear.style.cursor = 'pointer'; gear.style.marginLeft = '6px'; gear.title = 'Настройки панели'; const settingsPanel = document.createElement('div'); settingsPanel.style = 'background:#003b3b;color:white;padding:6px;border-radius:6px;position:absolute;right:0;top:120%;z-index:10001;display:none;font-size:12px;min-width:150px;box-shadow:0 0 6px black;'; settingsPanel.innerHTML = ` <div style="margin-bottom:6px;">📏 Размер панели:<br> <select id="panelSize"> <option value="0.8">Small</option> <option value="1">Medium</option> <option value="1.3">Large</option> </select> </div> <div>📍 Положение панели:<br> <select id="panelPos"> <option value="left">Слева</option> <option value="center">Посередине</option> <option value="right">Справа</option> </select> </div>`; gear.onclick = () => { settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none'; }; setTimeout(() => { settingsPanel.querySelector('#panelSize').value = panelScale; settingsPanel.querySelector('#panelPos').value = panelPosition; }, 0); settingsPanel.querySelector('#panelSize').onchange = e => { GM_setValue('panelSize', e.target.value); panel.style.transform = `scale(${e.target.value})`; panel.style.transformOrigin = panelPosition === 'left' ? 'top left' : (panelPosition === 'center' ? 'top center' : 'top right'); }; settingsPanel.querySelector('#panelPos').onchange = e => { GM_setValue('panelPos', e.target.value); const pos = e.target.value; panel.style.top = '0px'; // Фиксируем под самый верх panel.style.bottom = 'auto'; panel.style.left = pos === 'left' ? '10px' : (pos === 'center' ? '50%' : 'auto'); panel.style.right = pos === 'right' ? '10px' : 'auto'; panel.style.transformOrigin = pos === 'left' ? 'top left' : (pos === 'center' ? 'top center' : 'top right'); if (pos === 'center') panel.style.transform = `translateX(-50%) scale(${panelScale})`; else panel.style.transform = `scale(${panelScale})`; }; return { gear, settingsPanel }; } function createPanel() { const panel = document.createElement('div'); panel.id = 'customFontPanel'; const pos = panelPosition; panel.style = ` position:fixed; top:0px; /* Под самый верх */ bottom:auto; left:${pos === 'left' ? '10px' : (pos === 'center' ? '50%' : 'auto')}; right:${pos === 'right' ? '10px' : 'auto'}; background:#004c4c; color:white; padding:10px; border-radius:10px; z-index:10000; font-family:sans-serif; font-size:14px; box-shadow:0 0 10px rgba(0,0,0,0.3); display:none; min-width:200px; transform:${pos === 'center' ? `translateX(-50%) scale(${panelScale})` : `scale(${panelScale})`}; transform-origin:${pos === 'left' ? 'top left' : (pos === 'center' ? 'top center' : 'top right')}; `; const title = document.createElement('div'); title.textContent = '⚡ Профили:'; title.style.marginBottom = '4px'; panel.appendChild(title); const profiles = document.createElement('div'); profiles.style.display = 'flex'; profiles.style.flexWrap = 'wrap'; profiles.style.gap = '6px'; ['Минимум','Комфорт','Ночь'].forEach(p => { const btn = document.createElement('button'); btn.textContent = p; btn.style.cssText = 'padding: 4px 8px; border-radius: 6px; border: none; cursor: pointer; background: #089; color: #fff;'; btn.onclick = () => { if (p === 'Минимум') { GM_setValue('font','open-sans'); GM_setValue('size','12px'); } if (p === 'Комфорт') { GM_setValue('font','manrope'); GM_setValue('size','14px'); } if (p === 'Ночь') { GM_setValue('font','rubik'); GM_setValue('size','16px'); } location.reload(); }; profiles.appendChild(btn); }); panel.appendChild(profiles); const fontSelect = document.createElement('select'); for (const key in FONTS) { const opt = document.createElement('option'); opt.value = key; opt.textContent = FONTS[key]; opt.style.fontFamily = FONTS[key]; if (key === savedFont) opt.selected = true; fontSelect.appendChild(opt); } fontSelect.onchange = () => { GM_setValue('font', fontSelect.value); applyStyles(fontSelect.value, GM_getValue('size', '14px')); }; const sizeSelect = document.createElement('select'); ['12px','14px','16px','18px','20px'].forEach(px => { const opt = document.createElement('option'); opt.value = px; opt.textContent = px; if (px === savedSize) opt.selected = true; sizeSelect.appendChild(opt); }); sizeSelect.onchange = () => { GM_setValue('size', sizeSelect.value); applyStyles(GM_getValue('font', 'verdana'), sizeSelect.value); }; const radioSelect = document.createElement('select'); const none = document.createElement('option'); none.textContent = '-- Радио --'; none.value = ''; radioSelect.appendChild(none); for (const name in RADIO) { const opt = document.createElement('option'); opt.value = RADIO[name]; opt.textContent = name; if (RADIO[name] === savedRadio) opt.selected = true; radioSelect.appendChild(opt); } const audio = document.createElement('audio'); audio.controls = true; audio.volume = savedVolume; audio.style.width = '100%'; if (savedRadio) audio.src = savedRadio; radioSelect.onchange = () => { GM_setValue('radio', radioSelect.value); audio.src = radioSelect.value; audio.play(); GM_setValue('isPlaying', true); }; audio.onvolumechange = () => GM_setValue('volume', audio.volume); audio.ontimeupdate = () => { GM_setValue('currentTime', audio.currentTime); }; audio.onplay = () => GM_setValue('isPlaying', true); audio.onpause = () => GM_setValue('isPlaying', false); if (savedRadio && savedAutoplay) { setTimeout(() => { audio.play(); audio.currentTime = savedTime; }, 1000); if (savedTimer > 0) setTimeout(() => audio.pause(), savedTimer * 60000); } else if (savedRadio && savedPlaying) { setTimeout(() => { audio.play(); audio.currentTime = savedTime; }, 1000); } const timerBox = document.createElement('select'); timerBox.innerHTML = ` <option value="0">⏱ Без таймера</option> <option value="15">⏱ 15 мин</option> <option value="30">⏱ 30 мин</option> <option value="60">⏱ 60 мин</option> `; timerBox.value = savedTimer; timerBox.onchange = () => GM_setValue('autotimer', parseInt(timerBox.value)); const autoStart = document.createElement('label'); autoStart.style = 'display:flex;align-items:center;margin-top:5px;gap:4px;position:relative'; const autoCb = document.createElement('input'); autoCb.type = 'checkbox'; autoCb.checked = savedAutoplay; autoCb.onchange = () => GM_setValue('autoplay', autoCb.checked); autoStart.appendChild(autoCb); autoStart.appendChild(document.createTextNode('Автостарт')); const { gear, settingsPanel } = createPanelSettings(panel); autoStart.appendChild(gear); autoStart.appendChild(settingsPanel); panel.appendChild(fontSelect); panel.appendChild(sizeSelect); panel.appendChild(radioSelect); panel.appendChild(audio); panel.appendChild(timerBox); panel.appendChild(autoStart); document.body.appendChild(panel); } function createIconButton() { const button = document.createElement('div'); button.textContent = 'S'; button.style = 'position:fixed;top:20px;right:20px;width:40px;height:40px;background:#2e7d78;color:#fff;font-weight:bold;font-size:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 0 8px rgba(0,0,0,0.3);z-index:10001'; button.onclick = () => { const panel = document.getElementById('customFontPanel'); panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; }; document.body.appendChild(button); } applyStyles(savedFont, savedSize); window.addEventListener('load', () => { createPanel(); createIconButton(); }); })();