Kick.com Enhanced Video Seekbar

Makes seekbar clickable anywhere like Twitter/Twitch, including on thumb

  1. // ==UserScript==
  2. // @name Kick.com Enhanced Video Seekbar
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.4
  5. // @description Makes seekbar clickable anywhere like Twitter/Twitch, including on thumb
  6. // @author zxvc23
  7. // @match htts://kick.com/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. function initSeekbarEnhancement() {
  16. const seekbarContainer = document.querySelector('span[data-orientation="horizontal"].bg-subtle\\/50');
  17. if (!seekbarContainer) {
  18. console.log('Seekbar container not found');
  19. return;
  20. }
  21.  
  22. const progressBar = seekbarContainer.querySelector('span.bg-green-500');
  23. const thumb = document.querySelector('span[role="slider"][aria-label="Current video time"]');
  24.  
  25. if (!progressBar || !thumb) {
  26. console.log('Progress bar or thumb not found');
  27. return;
  28. }
  29.  
  30. const maxValue = parseInt(thumb.getAttribute('aria-valuemax'));
  31. console.log('Max value:', maxValue);
  32.  
  33. seekbarContainer.style.cursor = 'pointer';
  34. thumb.style.pointerEvents = 'none'; // Disable native thumb interactions
  35.  
  36. function handleSeekClick(e) {
  37. e.preventDefault();
  38. e.stopPropagation();
  39.  
  40. const rect = seekbarContainer.getBoundingClientRect();
  41. const clickPosition = e.clientX - rect.left;
  42. const totalWidth = rect.width;
  43. const percentage = Math.max(0, Math.min(1, clickPosition / totalWidth)); // Clamp between 0 and 1
  44.  
  45. const newTime = Math.floor(maxValue * percentage);
  46. console.log('Click at:', percentage * 100, '%, New time:', newTime);
  47.  
  48. // Update slider attributes
  49. thumb.setAttribute('aria-valuenow', newTime);
  50. thumb.parentElement.style.left = `calc(${percentage * 100}% + 0px)`;
  51. progressBar.style.right = `${100 - (percentage * 100)}%`;
  52.  
  53. // Dispatch multiple events to ensure player updates
  54. ['input', 'change'].forEach(eventType => {
  55. const event = new Event(eventType, { bubbles: true });
  56. thumb.dispatchEvent(event);
  57. });
  58.  
  59. // Force focus to potentially trigger player update
  60. thumb.focus();
  61. }
  62.  
  63. // Remove any existing listeners to avoid duplicates
  64. seekbarContainer.removeEventListener('click', handleSeekClick);
  65. thumb.removeEventListener('mousedown', preventDefault);
  66.  
  67. // Add click handler
  68. seekbarContainer.addEventListener('click', handleSeekClick, true);
  69.  
  70. // Prevent default thumb behavior
  71. function preventDefault(e) {
  72. e.preventDefault();
  73. e.stopPropagation();
  74. }
  75. thumb.addEventListener('mousedown', preventDefault, true);
  76. thumb.addEventListener('click', preventDefault, true);
  77. }
  78.  
  79. // Initialize and reinitialize on DOM changes
  80. function setupObserver() {
  81. initSeekbarEnhancement();
  82.  
  83. const observer = new MutationObserver(() => {
  84. if (document.querySelector('span[data-orientation="horizontal"].bg-subtle\\/50')) {
  85. initSeekbarEnhancement();
  86. }
  87. });
  88.  
  89. observer.observe(document.body, {
  90. childList: true,
  91. subtree: true
  92. });
  93. }
  94.  
  95. window.addEventListener('load', setupObserver);
  96. })();