您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Control YouTube video volume by mouse wheel.
- // ==UserScript==
- // @name YouTube Volume Mouse Controller
- // @namespace wddd
- // @version 1.5.0
- // @author wddd
- // @license MIT
- // @description Control YouTube video volume by mouse wheel.
- // @homepage https://github.com/wdwind/YouTubeVolumeMouseController
- // @match *://www.youtube.com/*
- // ==/UserScript==
- var ytb = {
- inIframe: function() {
- try {
- return window.self !== window.top;
- } catch (e) {
- return true;
- }
- },
- isEmbed: function() {
- return this.inIframe || window.location.href.includes('youtube.com/embed');
- },
- getPlayerElement: function() {
- if (!this.isEmbed()) {
- var ytd_player = document.getElementsByTagName("ytd-player");
- for (var player of ytd_player) {
- if (player.getPlayer() && player.className.indexOf('preview') == -1) {
- return player;
- }
- }
- } else {
- return this.getPlayer();
- }
- },
- getVideo: function() {
- return this.getPlayerElement()?.getElementsByTagName("video")[0];
- },
- getPlayer: function() {
- if (!this.isEmbed()) {
- return this.getPlayerElement()?.getPlayer();
- } else {
- return document.getElementsByClassName("html5-video-player")[0];
- }
- },
- ready: function() {
- return this.getPlayer() && this.getVideo();
- }
- }
- function run() {
- if (!ytb.getPlayer()) {
- // eslint-disable-next-line no-console
- console.log("Player not found (yet).");
- return;
- }
- var timer = 0;
- // detect available wheel event
- var support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
- document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
- "DOMMouseScroll"; // let"s assume that remaining browsers are older Firefox
- ytb.getVideo().addEventListener(support, function (event) {
- var volume = ytb.getPlayer().getVolume();
- var volumeDelta = 5;
- var deltaY = support == "mousewheel" ? event.wheelDelta : (event.deltaY || event.detail);
- // Optimize volume change for touchpad
- if (Math.abs(deltaY) < 5) {
- volumeDelta = Math.max(Math.floor(Math.abs(deltaY)), 1);
- }
- volume += (deltaY > 0 ? -volumeDelta : volumeDelta);
- // Limit the volume between 0 and 100
- volume = Math.max(0, Math.min(100, volume));
- if (volume > 0) {
- ytb.getPlayer().unMute(true);
- }
- ytb.getPlayer().setVolume(volume, true);
- timer = showSlider(timer, volume);
- // Prevent the page to scroll
- event.preventDefault();
- event.stopImmediatePropagation();
- return false;
- });
- }
- function showSlider(timer, volume) {
- if (timer) {
- clearTimeout(timer);
- }
- var sliderBar = appendSlideBar();
- sliderBar.style.display = "block";
- timer = setTimeout(function () {
- sliderBar.style.display = "none";
- }, 1000);
- sliderBar.innerText = "Volume: " + volume;
- return timer;
- }
- function appendSlideBar() {
- var sliderBar = document.getElementById("sliderBar");
- if (!sliderBar) {
- var sliderBarElement = document.createElement("div");
- sliderBarElement.id = "sliderBar";
- ytb.getPlayerElement().getElementsByClassName("html5-video-container")[0].appendChild(sliderBarElement);
- sliderBar = document.getElementById("sliderBar");
- addCss(sliderBar, {
- "width": "100%",
- "height": "20px",
- "position": "relative",
- "z-index": "9999",
- "text-align": "center",
- "color": "#fff",
- "font-size": "initial",
- "opacity": "0.9",
- "background-color": "rgba(0,0,0,0.2)",
- });
- }
- if (!ytb.isEmbed()) {
- addCss(sliderBar, {"top": getSliderBarTopProp() + "px"});
- }
- return sliderBar;
- }
- function addCss(element, css) {
- for (var cssAttr in css) {
- element.style[cssAttr] = css[cssAttr];
- }
- }
- function getSliderBarTopProp() {
- var fullScreenTitleHeight = 0;
- var fullScreenTitle = document.getElementsByClassName("ytp-title")[0];
- if (fullScreenTitle && fullScreenTitle.offsetParent) {
- fullScreenTitleHeight = fullScreenTitle.offsetHeight;
- }
- var videoTop = ytb.getVideo().getBoundingClientRect().top;
- var headerBoundingBox =
- (document.getElementById("masthead-positioner") || document.getElementById("masthead-container")).getBoundingClientRect();
- var headerTop = headerBoundingBox.top;
- var headerHeight = headerBoundingBox.height;
- var overlap = (headerHeight + headerTop > 0) ? Math.max(0, headerHeight - videoTop) : 0;
- return Math.max(fullScreenTitleHeight, overlap);
- }
- /**
- * YouTube use Javascript to navigate between pages. So the script will not work:
- * 1. If the script only includes/matches the sub pages (like the video page www.youtube.com/watch?v=...)
- * 2. And the user navigates to the sub page from a page which is not included/matched by the script
- *
- * In the above scenario, the script will not be executed.
- *
- * To run the script in all cases,
- * 1. Include/match the whole YouTube host
- * 2. Detect Javascript events, and run the script appropriately
- *
- * Details:
- * * https://stackoverflow.com/questions/32275387/recall-tampermonkey-script-when-page-location-changes/32277150#32277150
- * * https://stackoverflow.com/questions/34077641/how-to-detect-page-navigation-on-youtube-and-modify-html-before-page-is-rendered
- * * https://github.com/1c7/Youtube-Auto-Subtitle-Download/blob/master/Youtube-Subtitle-Downloader/Tampermonkey.js#L122-L152
- */
- // trigger when navigating to new material design page
- window.addEventListener("yt-navigate-finish", function () {
- if (window.location.href.includes("/watch?v=")) {
- run();
- }
- });
- // trigger when navigating to the old page
- window.addEventListener("spfdone", function () {
- if (window.location.href.includes("/watch?v=")) {
- run();
- }
- });
- // trigger when directly loading the page
- window.addEventListener("DOMContentLoaded", function () {
- if (window.location.href.includes("/watch?v=")) {
- run();
- }
- });
- /**
- * Use MutationObserver to cover all edge cases.
- * https://stackoverflow.com/a/39803618
- *
- * This is to handle the use case where navigation happens but <video> has not been loaded yet.
- * (In YouTube the contents are loaded asynchronously.)
- */
- var observer = new MutationObserver(function() {
- if (ytb.ready()) {
- observer.disconnect();
- run();
- }
- });
- observer.observe(document.body, /* config */ {childList: true, subtree: true});