// ==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);
})();