Greasy Fork is available in English.
Mouse wheel rewind over YouTube video
// ==UserScript==
// @name YT Scroll Rewind
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Mouse wheel rewind over YouTube video
// @author You
// @match https://www.youtube.com/watch*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// ==/UserScript==
(function () {
'use strict';
// === Config / Настройки ===
const config = {
seekTime: 5, // Seconds to seek per wheel step / Секунд за один шаг колеса
deadZonePercent: 20 // Ignore edges (%) / Игнорировать края плеера (%)
};
// YouTube player container / Контейнер плеера YouTube
function getPlayer() {
return document.querySelector('.html5-video-player');
}
// HTML5 <video> element / Сам элемент видео
function getVideo() {
return document.querySelector('video');
}
// Check cursor inside player rect / Курсор внутри плеера
function isInside(e, rect) {
return (
e.clientX >= rect.left && e.clientX <= rect.right &&
e.clientY >= rect.top && e.clientY <= rect.bottom
);
}
// Dead zone near edges (avoid conflicts with UI) /
// Мёртвая зона по краям (чтобы не мешать интерфейсу)
function isInDeadZone(e, rect) {
const dz = config.deadZonePercent / 100;
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
return (
x < rect.width * dz ||
x > rect.width * (1 - dz) ||
y < rect.height * dz ||
y > rect.height * (1 - dz)
);
}
// Main wheel handler / Основная логика прокрутки
function handleWheel(e) {
const player = getPlayer();
const video = getVideo();
if (!player || !video) return;
const rect = player.getBoundingClientRect();
if (!isInside(e, rect)) return;
if (isInDeadZone(e, rect)) return;
// Block YouTube default scroll behavior /
// Блокируем стандартное поведение YouTube
e.preventDefault();
e.stopImmediatePropagation();
// Scroll up = back, down = forward /
// Вверх — назад, вниз — вперёд
video.currentTime += e.deltaY < 0 ? -config.seekTime : config.seekTime;
// Clamp to video duration /
// Ограничение в пределах видео
video.currentTime = Math.max(0, Math.min(video.duration, video.currentTime));
}
// Capture phase + passive:false is REQUIRED /
// Захват + passive:false ОБЯЗАТЕЛЬНЫ
document.addEventListener('wheel', handleWheel, {
passive: false,
capture: true
});
// Optional external access to config /
// Необязательный доступ к настройкам извне
window.ytScrollConfig = config;
})();