您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Use play/pause/ArrowRight/ArrowLeft keyboard btns anywhere, but not only when youtube player is in focus. Additionaly use '[' and ']' keys to change playbackRate
当前为
// ==UserScript== // @name Youtube player keyboard controls // @description Use play/pause/ArrowRight/ArrowLeft keyboard btns anywhere, but not only when youtube player is in focus. Additionaly use '[' and ']' keys to change playbackRate // @author [email protected] // @license MIT // @version 0.2 // @include https://www.youtube.com/* // @namespace https://gf.zukizuki.org/users/174246 // ==/UserScript== ;(function(window, undefined) { const defaultYoutubeMoviePlayerId = 'movie_player'; let youtubeMoviePlayerId; const getYoutubeMoviePlayerId = () => { let youtubeMoviePlayerId = defaultYoutubeMoviePlayerId; try { if ( typeof ytplayer !== 'undefined' ) { youtubeMoviePlayerId = ytplayer.config.attrs.id || defaultYoutubeMoviePlayerId; } } catch(e) { youtubeMoviePlayerId = defaultYoutubeMoviePlayerId; } return youtubeMoviePlayerId; }; const getYoutubePlayer = (getVideoEl = true) => { if ( youtubeMoviePlayerId === void 0 ) { youtubeMoviePlayerId = getYoutubeMoviePlayerId(); } const $moviePlayer = document.getElementById(youtubeMoviePlayerId); if ( !$moviePlayer ) { return null; } return getVideoEl ? $moviePlayer.querySelector('video') : $moviePlayer ; }; const getParrentByClassName = ($el, className, maxTopEls = 15) => { if ( !$el ) { return null; } let i = 0; let child = $el; for ( ; i < maxTopEls ; i++ ) { if ( child.classList.contains(className) ) { return child; } if ( !child.parentElement ) { return null; } child = child.parentElement; } return null; }; const isAdvVideoPlayer = $moviePlayer => { if ( !$moviePlayer ) { return false; } const $moviePlayerWrapper = getParrentByClassName($moviePlayer, 'html5-video-player', 5); if ( $moviePlayerWrapper ) { return $moviePlayerWrapper.classList.contains('ad-showing'); } return false; }; const isEditable = $el => { return $el.tagName === 'INPUT' || $el.tagName === 'TEXTAREA' || ($el.hasAttribute('contenteditable') && $el.contentEditable !== 'false' && $el.contentEditable !== 'inherit'); }; const isVideoFullscreenElement = $el => { const fullScreenElement = document.mozFullScreenElement || document.webkitFullscreenElement || document.webkitCurrentFullScreenElement || document.fullscreenElement; const videoEl = fullScreenElement && fullScreenElement.querySelector('video'); return videoEl && $el.querySelector('video') !== videoEl && videoEl.tagName === 'VIDEO'; }; const _checkParent = $el => { if ( !$el ) { return false; } let i = 0; let hasParent = true; let child = $el; for ( ; i < 20 ; i++ ) { if ( !child.parentElement ) { hasParent = false; break; } child = child.parentElement; } return hasParent; }; let playbackTimer; let playbackRateElId; let $elPlaybackRate; let playbackRateOnKeyDown = event => { const $moviePlayer = getYoutubePlayer(false); if ( !$moviePlayer ) { return false; } const {code, target} = event; if ( code === 'BracketRight' || code === 'BracketLeft' ) { if ( isEditable(target) ) { return; } /*if ( !isVideoFullscreenElement(target) && (target == $moviePlayer || isEditable(target)) ) { //console.log(' return ', 1) return; } */ if ( !playbackRateElId && $moviePlayer ) { playbackRateElId = 'playbackRateText' + (Math.random() * 9e7 | 0).toString(36); $moviePlayer.insertAdjacentHTML('afterbegin', `<div id="${playbackRateElId}" style="position: absolute; z-index: 9999999; right: 20px; top: 20px; pointer-events: none; display: block; transition: opacity .5s; opacity: 0; color: yellow; width: auto; height: 48px; line-height: 48px; font-size: 48px; text-align: center; text-shadow: 1px 1px 4px #000;"></div>`); $elPlaybackRate = document.getElementById(playbackRateElId); } else if ( !_checkParent($elPlaybackRate) ) { // Unattachment element $moviePlayer.insertAdjacentElement('afterbegin', $elPlaybackRate); } const $video = $moviePlayer.querySelector('video'); const {playbackRate} = $video; let newPlaybackRate; { let delta = code === 'BracketLeft' ? -0.25 : 0.25; if ( delta < 0 ) { if ( playbackRate > 2 || playbackRate <= 1 ) { delta = -0.1; } } else { if ( playbackRate >= 2 || playbackRate < 1 ) { delta = 0.1; } } newPlaybackRate = playbackRate + delta; if ( newPlaybackRate < 0.5 ) { newPlaybackRate = 0.5; } else if ( newPlaybackRate > 3.5 ) { newPlaybackRate = 3.5; } // Округление до 2го знака после запятой newPlaybackRate = parseFloat(newPlaybackRate.toFixed(2)); } $video.playbackRate = newPlaybackRate; $elPlaybackRate.textContent = 'x' + newPlaybackRate; $elPlaybackRate.style.opacity = 1; if ( playbackTimer ) { clearTimeout(playbackTimer); } playbackTimer = setTimeout(() => { playbackTimer = void 0; $elPlaybackRate.style.opacity = 0; }, 500); return true; } }; if ( window.__onKey__ ) { document.removeEventListener('keyup', window.__onKey__, true); document.removeEventListener('keydown', window.__onKey__, true); window.__onKey__ = void 0; } const isNeedMagicActionsForYoutubeFix = () => { return String(HTMLMediaElement.prototype.play).indexOf('pauseVideo') !== -1; }; let prevVideoElementUrl; const fixPauseVideo = ($moviePlayer, $videoElement) => { const youtubePlayerControls = $moviePlayer.querySelector('.ytp-chrome-controls'); if ( youtubePlayerControls && prevVideoElementUrl !== $videoElement.src ) { prevVideoElementUrl = $videoElement.src; youtubePlayerControls.click(); } }; const sDoNotHandle = typeof Symbol === 'undefine' ? '__sDoNotHandle__' : Symbol('sDoNotHandle'); const onKey = event => { if ( event[sDoNotHandle] ) { return; } const $moviePlayer = getYoutubePlayer(false); if ( !$moviePlayer ) { return; } const {code, target, keyCode, charCode, which} = event; if ( code === 'Space' || code === 'ArrowRight' || code === 'ArrowLeft' ) { if ( isAdvVideoPlayer($moviePlayer) ) { // Проигрывается реклама // TODO:: нужно сделать кастомную перемотку вперёд-назад и кнопку "Пропустить" console.log('Youtube Adw mode'); } if ( !isVideoFullscreenElement(target) && (/*target == $moviePlayer || */isEditable(target)) ) { //console.log(' return ', 1) return; } const newEvent = new KeyboardEvent(event.type, event); try { if ( newEvent.keyCode !== keyCode ) { Object.defineProperty(newEvent, 'keyCode', {value: keyCode, configurable: true, enumerable: true, writable: false}); } } catch(e){} try { if ( newEvent.charCode !== charCode ) { Object.defineProperty(newEvent, 'charCode', {value: charCode, configurable: true, enumerable: true, writable: false}); } } catch(e){} try { if ( newEvent.which !== which ) { Object.defineProperty(newEvent, 'which', {value: which, configurable: true, enumerable: true, writable: false}); } } catch(e){} newEvent[sDoNotHandle] = true; { const $videoElement = getYoutubePlayer(true); if ( $videoElement.paused && isNeedMagicActionsForYoutubeFix() ) { fixPauseVideo($moviePlayer, $videoElement); } //console.log(' dispatchEvent ', 2, newEvent, event); $videoElement.dispatchEvent(newEvent); } event.stopPropagation(); event.preventDefault(); } else if ( event.type === 'keydown' ) { if ( playbackRateOnKeyDown(event) ) { event.stopPropagation(); event.preventDefault(); } } }; document.addEventListener('keyup', onKey, true); document.addEventListener('keydown', onKey, true); window.__onKey__ = onKey; })(window);