您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Sync with native YouTube volume & avoid duplicate seeking
当前为
- // ==UserScript==
- // @name YouTube Arrow Key Video Control (Improved Sync)
- // @name:ru Улучшенное управление YouTube через стрелки
- // @namespace http://tampermonkey.net/
- // @version 2.1
- // @description Sync with native YouTube volume & avoid duplicate seeking
- // @description:ru Правильное управление видео YouTube через стрелки на клавиатуре.
- // @author Boss of this gym
- // @match *://www.youtube.com/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- const VOLUME_STEP = 10; // 10%
- const SEEK_STEP = 5; // seconds
- function getVideoElement() {
- return document.querySelector('video');
- }
- function getVolumePercent(video) {
- return Math.round(video.volume * 100);
- }
- function setVolumeFromPercent(video, percent) {
- const clamped = Math.min(Math.max(percent, 0), 100);
- video.volume = clamped / 100;
- showOverlay(`🔊 ${clamped}%`);
- }
- function seekVideo(video, delta) {
- const newTime = Math.min(Math.max(video.currentTime + delta, 0), video.duration);
- video.currentTime = newTime;
- showOverlay(`${delta > 0 ? '⏩' : '⏪'} ${Math.abs(delta)}s`);
- }
- function isInputElementFocused() {
- const active = document.activeElement;
- return active && (['INPUT', 'TEXTAREA'].includes(active.tagName) || active.isContentEditable);
- }
- function createOverlay() {
- const overlay = document.createElement('div');
- overlay.id = 'yt-ctrl-overlay';
- overlay.style.cssText = `
- position: fixed;
- top: 20%;
- left: 50%;
- transform: translateX(-50%);
- padding: 12px 24px;
- background: rgba(0, 0, 0, 0.7);
- color: #fff;
- font-size: 20px;
- border-radius: 8px;
- z-index: 9999;
- display: none;
- `;
- document.body.appendChild(overlay);
- return overlay;
- }
- const overlay = createOverlay();
- let overlayTimeout = null;
- function showOverlay(text) {
- overlay.textContent = text;
- overlay.style.display = 'block';
- clearTimeout(overlayTimeout);
- overlayTimeout = setTimeout(() => {
- overlay.style.display = 'none';
- }, 800);
- }
- window.addEventListener('keydown', function(event) {
- if (isInputElementFocused() || event.altKey) return;
- const video = getVideoElement();
- if (!video) return;
- if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {
- event.preventDefault();
- event.stopImmediatePropagation();
- if (document.activeElement !== video) {
- video.setAttribute('tabindex', '-1');
- video.focus();
- }
- switch (event.key) {
- case 'ArrowUp': {
- const vol = Math.floor(video.volume * 100);
- setVolumeFromPercent(video, vol + VOLUME_STEP);
- break;
- }
- case 'ArrowDown': {
- const vol = Math.floor(video.volume * 100);
- setVolumeFromPercent(video, vol - VOLUME_STEP);
- break;
- }
- case 'ArrowRight':
- seekVideo(video, SEEK_STEP);
- break;
- case 'ArrowLeft':
- seekVideo(video, -SEEK_STEP);
- break;
- }
- }
- }, true); // Use capture to intercept before YouTube
- })();