🏠 Home 

Kick Volume Wheel Control

Use the mouse wheel to adjust the volume. Middle-click to mute or unmute the player. Volume slider always visible.


Install this script?
  1. // ==UserScript==
  2. // @name Kick Volume Wheel Control
  3. // @namespace https://github.com/pabli24
  4. // @version 1.0.2
  5. // @description Use the mouse wheel to adjust the volume. Middle-click to mute or unmute the player. Volume slider always visible.
  6. // @author Pabli
  7. // @license MIT
  8. // @match https://kick.com/*
  9. // @icon data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgdmlld0JveD0iMCAwIDc1IDc1Ij4NCjxwYXRoIGQ9Ik0zOS4zODksMTMuNzY5IEwyMi4yMzUsMjguNjA2IEw2LDI4LjYwNiBMNiw0Ny42OTkgTDIxLjk4OSw0Ny42OTkgTDM5LjM4OSw2Mi43NSBMMzkuMzg5LDEzLjc2OXoiIHN0eWxlPSJzdHJva2U6IzUzZmMxODtzdHJva2Utd2lkdGg6NTtzdHJva2UtbGluZWpvaW46cm91bmQ7ZmlsbDojNTNmYzE4OyIvPg0KPHBhdGggZD0iTTQ4LDI3LjZhMTkuNSwxOS41IDAgMCAxIDAsMjEuNE01NS4xLDIwLjVhMzAsMzAgMCAwIDEgMCwzNS42TTYxLjYsMTRhMzguOCwzOC44IDAgMCAxIDAsNDguNiIgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzUzZmMxODtzdHJva2Utd2lkdGg6NTtzdHJva2UtbGluZWNhcDpyb3VuZCIvPg0KPC9zdmc+
  10. // @grant none
  11. // ==/UserScript==
  12. (function() {
  13. "use strict";
  14. const CONFIG = {
  15. VOLUME_STEP: 1,
  16. SHOW_CONTROLS_ON_SCROLL: true,
  17. SLIDER_ALWAYS_VISIBLE: true,
  18. HIDE_CURSOR_DELAY: 4000
  19. };
  20. function setCookie(name, value, days) {
  21. const date = new Date();
  22. date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
  23. document.cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`;
  24. }
  25. function prevent(e) {
  26. e.preventDefault();
  27. e.stopPropagation();
  28. e.stopImmediatePropagation();
  29. }
  30. const observer = new MutationObserver(mutations => {
  31. const video = document.getElementById("video-player");
  32. if (!video) return;
  33. wheel(video);
  34. });
  35. observer.observe(document.body, { childList: true, subtree: true });
  36. function wheel(video) {
  37. const videoDiv = document.querySelector("#injected-embedded-channel-player-video > div");
  38. if (videoDiv.hasAttribute("kpvolume")) return;
  39. videoDiv.setAttribute("kpvolume", "");
  40. videoDiv.addEventListener("wheel", (event) => {
  41. prevent(event);
  42. if (CONFIG.SHOW_CONTROLS_ON_SCROLL === true) {
  43. const showEvent = new Event('mousemove');
  44. videoDiv.dispatchEvent(showEvent);
  45. }
  46. if (video.muted && videoDiv.getAttribute("kpvolume")) {
  47. video.muted = false;
  48. setTimeout(() => {
  49. video.volume = videoDiv.getAttribute("kpvolume");
  50. slider();
  51. }, 50)
  52. } else if (event.deltaY < 0) {
  53. video.volume = Math.min(1, video.volume + (CONFIG.VOLUME_STEP / 100)); // Increase volume
  54. } else if (event.deltaY > 0) {
  55. video.volume = Math.max(0, video.volume - (CONFIG.VOLUME_STEP / 100)); // Decrease volume
  56. }
  57. setTimeout(slider, 50);
  58. setTimeout(() => setCookie("volume", video.volume, 365), 3000);
  59. });
  60. let hideCursorTimeout;
  61. videoDiv.addEventListener("mousemove", (event) => {
  62. setTimeout(() => {
  63. muteBtn();
  64. slider();
  65. }, 50)
  66. setTimeout(() => setCookie("volume", video.volume, 365), 3000);
  67. if (videoDiv.contains(event.target)) {
  68. videoDiv.style.cursor = 'default';
  69. if (hideCursorTimeout) clearTimeout(hideCursorTimeout);
  70. hideCursorTimeout = setTimeout(() => {
  71. videoDiv.style.cursor = 'none';
  72. }, CONFIG.HIDE_CURSOR_DELAY);
  73. }
  74. });
  75. videoDiv.addEventListener("mouseenter", (event) => {
  76. setTimeout(() => setCookie("volume", video.volume, 365), 100);
  77. });
  78. function slider() {
  79. const controls = videoDiv.querySelector('div > div.z-controls');
  80. if (!controls) return;
  81. const sliderFill = controls.querySelector('span[style*="right:"]'); // style="left: 0%; right: 40%;"
  82. const videoVolume = Math.round(video.volume * 100);
  83. sliderFill.style.right = `${100 - videoVolume}%`;
  84. const sliderThumb = controls.querySelector('span[style*="transform: var(--radix-slider-thumb-transform)"]'); // left: calc(40% + 1.6px);
  85. const offset = 8 + (videoVolume / 100) * -16;
  86. sliderThumb.style.left = `calc(${videoVolume}% + ${offset}px)`;
  87. const sliderValuenow = controls.querySelector('span[aria-valuenow]'); // aria-valuenow="40"
  88. sliderValuenow.setAttribute("aria-valuenow", videoVolume);
  89. const sliderP = controls.querySelector('.group\\/volume .betterhover\\:group-hover\\/volume\\:flex');
  90. sliderP.setAttribute("playervolume", videoVolume + "%");
  91. }
  92. function muteBtn() {
  93. const muteButton = document.querySelector('#injected-embedded-channel-player-video > div > div.z-controls .group\\/volume > button');
  94. if (!muteButton) return;
  95. muteButton.addEventListener("click", (event) => {
  96. prevent(event);
  97. mute();
  98. });
  99. }
  100. function mute() {
  101. if (video.muted) {
  102. video.muted = false;
  103. setTimeout(() => {
  104. video.volume = videoDiv.getAttribute("kpvolume");
  105. slider();
  106. }, 50)
  107. } else {
  108. videoDiv.setAttribute("kpvolume", video.volume);
  109. video.muted = true;
  110. }
  111. }
  112. videoDiv.addEventListener("mousedown", ({ button }) => {
  113. if (event.button === 1) {
  114. prevent(event);
  115. mute();
  116. }
  117. });
  118. document.addEventListener("keydown", (event) => {
  119. if ((event.key === 'M' || event.key === 'm') && event.target.tagName !== 'INPUT' && event.target.tagName !== 'TEXTAREA' && event.target.isContentEditable !== true) {
  120. prevent(event);
  121. mute();
  122. }
  123. });
  124. }
  125. let styles = `
  126. #injected-embedded-channel-player-video > div > div.z-controls .group\\/volume .betterhover\\:group-hover\\/volume\\:flex::after {
  127. content: attr(playervolume);
  128. font-weight: 600;
  129. font-size: .875rem;
  130. line-height: 1.25rem;
  131. margin-left: .5rem;
  132. width: 4ch;
  133. }`
  134. if (CONFIG.SLIDER_ALWAYS_VISIBLE === true) {
  135. styles += `
  136. #injected-embedded-channel-player-video > div > div.z-controls .group\\/volume .betterhover\\:group-hover\\/volume\\:flex {
  137. display: flex;
  138. align-items: center;
  139. }`
  140. }
  141. const styleSheet = document.createElement("style");
  142. styleSheet.textContent = styles;
  143. document.head.appendChild(styleSheet);
  144. })();