// ==UserScript== // @name HDREZKA Premium Player: Кино-шторки, Хоткеи и Автоплей // @name:en HDREZKA Premium Player: Cinema Zones, Hotkeys & AutoPlay // @namespace http://tampermonkey.net // @version 3.0 // @description Боковые градиентные шторки и Alt+Стрелки для переключения серий, автоплей, наглядная индикация переключения серии, перемотки и громкости в стильном HUD-интерфейсе! // @description:en Side gradient zones & Alt+Arrows for switching episodes, autoplay, clear on-screen indicators for episodes, rewind, and volume in a sleek HUD interface! // @author Darkness-83 (совместно с AI) // @include /^https?://([^/]+\.)?(hdrezka|rezka|kinopub|hdbaza)[^/]*\./ // @run-at document-start // @grant none // @license GPL-3.0 // ==/UserScript== (function() { 'use strict'; // 1. ИНЪЕКЦИЯ СТИЛЕЙ ОФОРМЛЕНИЯ ПЛЕЕРА И ШТОРК const style = document.createElement('style'); style.textContent = ` #ps-overlay-wrap { display: none !important; opacity: 0 !important; } #cdn-player, #player, .b-player, #player_html5 { position: relative !important; } #cdn-player video, :fullscreen video { position: relative !important; z-index: 0 !important; } /* ПОЛНАЯ АДАПТИВНОСТЬ: ширина шторки всегда ровно 15% от ширины видеоэкрана */ .rezka-nav-zone { position: absolute !important; top: 0 !important; bottom: 0 !important; height: 100% !important; width: 15% !important; z-index: 10 !important; background: transparent !important; transition: background 0.3s ease, opacity 0.4s ease !important; opacity: 0; pointer-events: none; user-select: none !important; } #rezka-zone-prev { left: 0 !important; } #rezka-zone-next { right: 0 !important; } .rezka-nav-zone.visible { opacity: 1 !important; } /* ЦЕПОЧКА ПЛАВНЫХ ШАГОВ — сочный цвет у края и идеальное бесшовное рассеивание к центру */ #rezka-zone-prev:hover { background: linear-gradient(to right, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.80) 20%, rgba(0,0,0,0.60) 45%, rgba(0,0,0,0.30) 70%, rgba(0,0,0,0.10) 85%, rgba(0,0,0,0) 100%) !important; } #rezka-zone-next:hover { background: linear-gradient(to left, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.80) 20%, rgba(0,0,0,0.60) 45%, rgba(0,0,0,0.30) 70%, rgba(0,0,0,0.10) 85%, rgba(0,0,0,0) 100%) !important; } /* ЗОНА КЛИКА: занимает всю ширину шторки (15% от края видео) */ .rezka-click-target { position: absolute !important; width: 100% !important; top: 10% !important; bottom: 25% !important; cursor: pointer !important; pointer-events: none; z-index: 11 !important; } #rezka-zone-prev .rezka-click-target { left: 0 !important; } #rezka-zone-next .rezka-click-target { right: 0 !important; } .rezka-nav-zone.visible .rezka-click-target { pointer-events: auto !important; } /* Изолированный приоритет кнопок управления плеера */ #cdn-player [class*="pjs-"], #cdn-player [id*="pjs_"], #cdn-player [class*="control"], #cdn-player [class*="bar"], #cdn-player [class*="panel"], #cdn-player [class*="settings"], #cdn-player [class*="button"], :fullscreen [class*="pjs-"], :fullscreen [id*="pjs_"], :fullscreen [class*="control"], :fullscreen [class*="bar"] { z-index: 2000000000 !important; pointer-events: auto !important; } .rezka-nav-zone, .rezka-click-target { z-index: 10 !important; } .rezka-click-target { z-index: 11 !important; } `; const root = document.documentElement || document.head; if (root) root.appendChild(style); const btnPrev = document.createElement('div'); btnPrev.id = 'rezka-zone-prev'; btnPrev.className = 'rezka-nav-zone'; const btnNext = document.createElement('div'); btnNext.id = 'rezka-zone-next'; btnNext.className = 'rezka-nav-zone'; const clickTargetPrev = document.createElement('div'); clickTargetPrev.className = 'rezka-click-target'; clickTargetPrev.addEventListener('click', (e) => { switchEpisode('prev'); showZonesWithFade(); }, true); btnPrev.appendChild(clickTargetPrev); const clickTargetNext = document.createElement('div'); clickTargetNext.className = 'rezka-click-target'; clickTargetNext.addEventListener('click', (e) => { switchEpisode('next'); showZonesWithFade(); }, true); btnNext.appendChild(clickTargetNext); let mouseTimeout; let isScriptActive = false; function showZonesWithFade() { if (!isScriptActive) return; btnPrev.classList.add('visible'); btnNext.classList.add('visible'); clearTimeout(mouseTimeout); mouseTimeout = setTimeout(() => { btnPrev.classList.remove('visible'); btnNext.classList.remove('visible'); }, 3000); } function updateZonesGeometry(mainPlayer) { if (!isScriptActive) { if (btnPrev.parentNode) btnPrev.remove(); if (btnNext.parentNode) btnNext.remove(); return; } const fsElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement; const base = fsElement ? fsElement : mainPlayer; const video = base.querySelector('video:not(#ps-overlay-wrap video)'); if (!video) return; const videoParent = video.parentNode; if (btnNext.parentNode !== videoParent) { videoParent.appendChild(btnPrev); videoParent.appendChild(btnNext); } } // 2. ИНИЦИАЛИЗАЦИЯ И ИНТЕЛЛЕКТУАЛЬНЫЙ ТРЕКЕР ВИДИМОСТИ ПЛЕЕРА document.addEventListener('DOMContentLoaded', () => { window.showRezkaNotification = function(text, direction, isRewind = false, volumeStatus = '') { // НАСТРОЙКА ВРЕМЕНИ ОТОБРАЖЕНИЯ (в миллисекундах) const TIME_SERIES = 1800; // Сколько висит уведомление о СЕРИИ const TIME_REWIND = 800; // Сколько висит уведомление о ПЕРЕМОТКЕ и ГРОМКОСТИ const oldNotify = document.getElementById('hdrezka-switcher-notify'); if (oldNotify) oldNotify.remove(); const mainPlayer = document.querySelector('#cdn-player, #player, .b-player, #player_html5'); if (!mainPlayer) return; const notify = document.createElement('div'); notify.id = 'hdrezka-switcher-notify'; let contentHTML = ''; if (volumeStatus !== '') { // УВЕДОМЛЕНИЕ О ГРОМКОСТИ: Векторные SVG иконки let volumeIconSVG = ''; if (volumeStatus.includes('Выкл')) { volumeIconSVG = ` `; } else if (direction === 'next') { volumeIconSVG = ` `; } else { volumeIconSVG = ` `; } contentHTML = `
${volumeIconSVG} ${volumeStatus}
`; } else if (isRewind) { // ПЕРЕМОТКА: Стрелочки + 5с const rewindArrow = direction === 'next' ? '❯❯' : '❮❮'; const rewindText = direction === 'next' ? '+5с' : '-5с'; const innerLayout = direction === 'next' ? `${rewindText}${rewindArrow}` : `${rewindArrow}${rewindText}`; contentHTML = `
${innerLayout}
`; } else { // СЕРИЯ: Изящные тонкие SVG иконки «Следующий/Предыдущий трек» один в один со скрина let seriesIconSVG = ''; if (direction === 'next') { // Тонкие стрелочки вправо, упирающиеся в черту seriesIconSVG = ` `; } else { // Тонкие стрелочки влево, упирающиеся в черту seriesIconSVG = ` `; } contentHTML = `
${seriesIconSVG} ${text}
`; } notify.innerHTML = contentHTML; Object.assign(notify.style, { position: 'absolute', top: '20%', left: '50%', transform: 'translateX(-50%)', backgroundColor: 'transparent', padding: '10px 24px', borderRadius: '30px', zIndex: '2147483647', pointerEvents: 'none', transition: 'opacity 0.2s ease', opacity: '1', border: '1px solid rgba(255, 255, 255, 0.4)', boxShadow: 'inset 0 0 4px rgba(0,0,0,0.6), 0 2px 8px rgba(0,0,0,0.5)' }); const fullscreenEl = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement; if (fullscreenEl) fullscreenEl.appendChild(notify); else mainPlayer.appendChild(notify); const displayTime = (isRewind || volumeStatus !== '') ? TIME_REWIND : TIME_SERIES; const removeTime = displayTime + 300; setTimeout(() => { notify.style.opacity = '0'; }, displayTime); setTimeout(() => { notify.remove(); }, removeTime); }; const mainPlayer = document.querySelector('#cdn-player, #player, #player_html5, .b-player'); if (mainPlayer) { const visibilityTracker = new IntersectionObserver((entries) => { entries.forEach(entry => { isScriptActive = entry.isIntersecting; updateZonesGeometry(mainPlayer); }); }, { threshold: 0.05 }); visibilityTracker.observe(mainPlayer); mainPlayer.addEventListener('mousemove', () => { if (isScriptActive) { updateZonesGeometry(mainPlayer); showZonesWithFade(); } }, true); mainPlayer.addEventListener('click', showZonesWithFade, true); initAutoplayListener(mainPlayer); document.addEventListener('fullscreenchange', () => updateZonesGeometry(mainPlayer)); document.addEventListener('webkitfullscreenchange', () => updateZonesGeometry(mainPlayer)); } }); function forcePlayVideo() { let attempts = 0; const interval = setInterval(() => { const mainPlayer = document.querySelector('#cdn-player, #player, .b-player, #player_html5'); const video = mainPlayer ? mainPlayer.querySelector('video:not(#ps-overlay-wrap video)') : null; if (video) { if (video.paused) { video.play().catch(() => { const rect = video.getBoundingClientRect(); const ev = new MouseEvent('click', { clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, bubbles: true, cancelable: true }); video.dispatchEvent(ev); }); } if (!video.paused && video.currentTime > 0) clearInterval(interval); } if (++attempts >= 40) clearInterval(interval); }, 300); } function switchEpisode(direction) { let episodes = Array.from(document.querySelectorAll('.b-simple_episodes__list_item, [data-episode_id], .episodes-list a')); episodes = episodes.filter(el => !el.textContent.trim().toLowerCase().includes('трейлер')); if (!episodes.length) return; const activeIndex = episodes.findIndex(el => el.classList.contains('active') || el.classList.contains('current')); if (activeIndex === -1) return; let target = episodes[activeIndex + (direction === 'next' ? 1 : -1)]; if (target) { if (window.showRezkaNotification) window.showRezkaNotification(target.textContent.trim(), direction, false, ''); target.click(); setTimeout(forcePlayVideo, 500); } } function initAutoplayListener(mainPlayer) { if (mainPlayer.dataset.rezkaAutoplayBound) return; setInterval(() => { const video = mainPlayer.querySelector('video:not(#ps-overlay-wrap video)'); if (video && !video.dataset.rezkaEndTracked) { video.addEventListener('ended', () => { if (video.duration > 30) switchEpisode('next'); }); video.dataset.rezkaEndTracked = "true"; } }, 1000); mainPlayer.dataset.rezkaAutoplayBound = "true"; } // 3. ОБРАБОТЧИК КЛАВИШ КЛАВИАТУРЫ (С ТОЧНЫМ ПОДГНОНОМ ГРОМКОСТИ) document.addEventListener('keydown', function(e) { if (!isScriptActive) return; const activeEl = document.activeElement; if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) return; const mainPlayer = document.querySelector('#cdn-player, #player, .b-player, #player_html5'); const fsElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement; const base = fsElement ? fsElement : mainPlayer; const video = base ? base.querySelector('video:not(#ps-overlay-wrap video)') : null; // Серии (Alt + Стрелки Влево/Вправо) if (e.altKey) { if (e.keyCode === 39) { e.preventDefault(); e.stopPropagation(); switchEpisode('next'); } else if (e.keyCode === 37) { e.preventDefault(); e.stopPropagation(); switchEpisode('prev'); } return; } // Перемотка (Стрелки Влево/Вправо) if (e.keyCode === 39 || e.keyCode === 37) { if (video) { e.preventDefault(); e.stopPropagation(); if (e.keyCode === 39) { video.currentTime = Math.min(video.duration, video.currentTime + 5); if (window.showRezkaNotification) window.showRezkaNotification('', 'next', true, ''); } else if (e.keyCode === 37) { video.currentTime = Math.max(0, video.currentTime - 5); if (window.showRezkaNotification) window.showRezkaNotification('', 'prev', true, ''); } } return; } // Громкость (Стрелки Вверх/Вниз) с выводом ИТОГОВЫХ ПРОЦЕНТОВ (30%, 40%...) if (e.keyCode === 38 || e.keyCode === 40) { if (video) { e.preventDefault(); e.stopPropagation(); const targetToPike = video.parentNode || video; const fakeKeyEvent = new KeyboardEvent('keydown', { key: e.key, code: e.code, keyCode: e.keyCode, which: e.keyCode, bubbles: true, cancelable: true }); isScriptActive = false; targetToPike.dispatchEvent(fakeKeyEvent); isScriptActive = true; // Задержка 10мс, чтобы плеер PlayerJS успел применить значение setTimeout(() => { const exactVol = Math.round(video.volume * 100); // Получаем точные проценты от 0 до 100 let volStatusText = ''; if (exactVol <= 0) { volStatusText = '0% (Выкл.)'; } else if (exactVol >= 100) { volStatusText = '100% (Макс.)'; } else { volStatusText = exactVol + '%'; } if (window.showRezkaNotification) { const direction = e.keyCode === 38 ? 'next' : 'prev'; window.showRezkaNotification('', direction, false, volStatusText); } }, 10); } } }, true); })();