Video Enhancer with Advanced Control Panel

Enhances your video by applying filter to it with a control panel and additional features.

  1. // ==UserScript==
  2. // @name Video Enhancer with Advanced Control Panel
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.4
  5. // @author UMATTER
  6. // @description Enhances your video by applying filter to it with a control panel and additional features.
  7. // @match *://*/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_addStyle
  11. // @grant GM_registerMenuCommand
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // SVG Filters
  19. const svgFilters = `
  20. <svg id="svgfilters" aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  21. <defs>
  22. <filter id="none"></filter>
  23. <filter id="turbulence">
  24. <feTurbulence type="fractalNoise" baseFrequency="0.015" numOctaves="2" result="turbulence_3" data-filterId="3" />
  25. <feDisplacementMap xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" in2="turbulence_3" scale="25" />
  26. </filter>
  27. <filter id="squiggly">
  28. <feTurbulence id="turbulence1" baseFrequency="0.02" numOctaves="3" result="noise" seed="0" />
  29. <feDisplacementMap id="displacement" in="SourceGraphic" in2="noise" scale="6" />
  30. </filter>
  31. <filter id="squiggly-1">
  32. <feTurbulence id="turbulence2" baseFrequency="0.02" numOctaves="3" result="noise" seed="1" />
  33. <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" />
  34. </filter>
  35. <filter id="squiggly-2">
  36. <feTurbulence id="turbulence3" baseFrequency="0.02" numOctaves="3" result="noise" seed="2" />
  37. <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" />
  38. </filter>
  39. <filter id="squiggly-3">
  40. <feTurbulence id="turbulence4" baseFrequency="0.02" numOctaves="3" result="noise" seed="3" />
  41. <feDisplacementMap in="SourceGraphic" in2="noise" scale="8" />
  42. </filter>
  43. <filter id="squiggly-4">
  44. <feTurbulence id="turbulence5" baseFrequency="0.02" numOctaves="3" result="noise" seed="4" />
  45. <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" />
  46. </filter>
  47. </defs>
  48. </svg>
  49. `;
  50. document.body.insertAdjacentHTML('beforeend', svgFilters);
  51.  
  52. // Initialization
  53. const htmlElement = window.document.documentElement;
  54. let defaultEnhancerActive = GM_getValue('defaultEnhancerActive', false);
  55. let saturateValue = GM_getValue('saturateValue', 1.3);
  56. let brightnessValue = GM_getValue('brightnessValue', 1);
  57. let contrastValue = GM_getValue('contrastValue', 1);
  58.  
  59. function setFilterOnElement(element) {
  60. if (defaultEnhancerActive) {
  61. element.style.filter = `saturate(${saturateValue}) brightness(${brightnessValue}) contrast(${contrastValue})`;
  62. } else {
  63. element.style.filter = "none";
  64. }
  65. }
  66.  
  67. function toggleEnhancer() {
  68. defaultEnhancerActive = !defaultEnhancerActive;
  69. GM_setValue('defaultEnhancerActive', defaultEnhancerActive);
  70. setFilterOnElement(htmlElement);
  71. if (fullscreenElement) {
  72. setFilterOnElement(fullscreenElement);
  73. }
  74. alert(`Video Enhancer is now ${defaultEnhancerActive ? 'ON' : 'OFF'}`);
  75. }
  76.  
  77. function createControlPanel() {
  78. const controlPanel = document.createElement('div');
  79. controlPanel.style.position = 'fixed';
  80. controlPanel.style.bottom = '10px';
  81. controlPanel.style.right = '10px';
  82. controlPanel.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
  83. controlPanel.style.color = 'white';
  84. controlPanel.style.padding = '10px';
  85. controlPanel.style.borderRadius = '5px';
  86. controlPanel.style.zIndex = '10000';
  87. controlPanel.innerHTML = `
  88. <button id="toggleEnhancerBtn">Toggle Enhancer</button>
  89. <br/>
  90. <label>Saturate: <input type="range" id="saturateRange" min="1" max="3" step="0.1" value="${saturateValue}"><input type="number" id="saturateNumber" min="1" max="3" step="0.1" value="${saturateValue}" style="width: 50px; margin-left: 10px;"></label>
  91. <br/>
  92. <label>Brightness: <input type="range" id="brightnessRange" min="0.5" max="2" step="0.1" value="${brightnessValue}"><input type="number" id="brightnessNumber" min="0.5" max="2" step="0.1" value="${brightnessValue}" style="width: 50px; margin-left: 10px;"></label>
  93. <br/>
  94. <label>Contrast: <input type="range" id="contrastRange" min="0.5" max="2" step="0.1" value="${contrastValue}"><input type="number" id="contrastNumber" min="0.5" max="2" step="0.1" value="${contrastValue}" style="width: 50px; margin-left: 10px;"></label>
  95. <br/>
  96. <button id="exportPresetBtn">Export Preset</button>
  97. <button id="importPresetBtn">Import Preset</button>
  98. <input type="file" id="presetFileInput" style="display: none;">
  99. `;
  100. document.body.appendChild(controlPanel);
  101.  
  102. document.getElementById('toggleEnhancerBtn').addEventListener('click', toggleEnhancer);
  103.  
  104. const saturateRange = document.getElementById('saturateRange');
  105. const saturateNumber = document.getElementById('saturateNumber');
  106. saturateRange.addEventListener('input', (e) => {
  107. saturateValue = e.target.value;
  108. saturateNumber.value = saturateValue;
  109. GM_setValue('saturateValue', saturateValue);
  110. if (defaultEnhancerActive) {
  111. setFilterOnElement(htmlElement);
  112. if (fullscreenElement) {
  113. setFilterOnElement(fullscreenElement);
  114. }
  115. }
  116. });
  117. saturateNumber.addEventListener('input', (e) => {
  118. saturateValue = e.target.value;
  119. saturateRange.value = saturateValue;
  120. GM_setValue('saturateValue', saturateValue);
  121. if (defaultEnhancerActive) {
  122. setFilterOnElement(htmlElement);
  123. if (fullscreenElement) {
  124. setFilterOnElement(fullscreenElement);
  125. }
  126. }
  127. });
  128.  
  129. const brightnessRange = document.getElementById('brightnessRange');
  130. const brightnessNumber = document.getElementById('brightnessNumber');
  131. brightnessRange.addEventListener('input', (e) => {
  132. brightnessValue = e.target.value;
  133. brightnessNumber.value = brightnessValue;
  134. GM_setValue('brightnessValue', brightnessValue);
  135. if (defaultEnhancerActive) {
  136. setFilterOnElement(htmlElement);
  137. if (fullscreenElement) {
  138. setFilterOnElement(fullscreenElement);
  139. }
  140. }
  141. });
  142. brightnessNumber.addEventListener('input', (e) => {
  143. brightnessValue = e.target.value;
  144. brightnessRange.value = brightnessValue;
  145. GM_setValue('brightnessValue', brightnessValue);
  146. if (defaultEnhancerActive) {
  147. setFilterOnElement(htmlElement);
  148. if (fullscreenElement) {
  149. setFilterOnElement(fullscreenElement);
  150. }
  151. }
  152. });
  153.  
  154. const contrastRange = document.getElementById('contrastRange');
  155. const contrastNumber = document.getElementById('contrastNumber');
  156. contrastRange.addEventListener('input', (e) => {
  157. contrastValue = e.target.value;
  158. contrastNumber.value = contrastValue;
  159. GM_setValue('contrastValue', contrastValue);
  160. if (defaultEnhancerActive) {
  161. setFilterOnElement(htmlElement);
  162. if (fullscreenElement) {
  163. setFilterOnElement(fullscreenElement);
  164. }
  165. }
  166. });
  167. contrastNumber.addEventListener('input', (e) => {
  168. contrastValue = e.target.value;
  169. contrastRange.value = contrastValue;
  170. GM_setValue('contrastValue', contrastValue);
  171. if (defaultEnhancerActive) {
  172. setFilterOnElement(htmlElement);
  173. if (fullscreenElement) {
  174. setFilterOnElement(fullscreenElement);
  175. }
  176. }
  177. });
  178.  
  179. document.getElementById('exportPresetBtn').addEventListener('click', () => {
  180. const preset = {
  181. saturateValue: saturateValue,
  182. brightnessValue: brightnessValue,
  183. contrastValue: contrastValue,
  184. };
  185. const blob = new Blob([JSON.stringify(preset)], {type: "application/json"});
  186. const url = URL.createObjectURL(blob);
  187. const a = document.createElement('a');
  188. a.href = url;
  189. a.download = 'preset.json';
  190. document.body.appendChild(a);
  191. a.click();
  192. document.body.removeChild(a);
  193. URL.revokeObjectURL(url);
  194. });
  195.  
  196. document.getElementById('importPresetBtn').addEventListener('click', () => {
  197. document.getElementById('presetFileInput').click();
  198. });
  199.  
  200. document.getElementById('presetFileInput').addEventListener('change', (e) => {
  201. const file = e.target.files[0];
  202. if (file) {
  203. const reader = new FileReader();
  204. reader.onload = (event) => {
  205. const preset = JSON.parse(event.target.result);
  206. saturateValue = preset.saturateValue || saturateValue;
  207. brightnessValue = preset.brightnessValue || brightnessValue;
  208. contrastValue = preset.contrastValue || contrastValue;
  209. GM_setValue('saturateValue', saturateValue);
  210. GM_setValue('brightnessValue', brightnessValue);
  211. GM_setValue('contrastValue', contrastValue);
  212. saturateRange.value = saturateValue;
  213. saturateNumber.value = saturateValue;
  214. brightnessRange.value = brightnessValue;
  215. brightnessNumber.value = brightnessValue;
  216. contrastRange.value = contrastValue;
  217. contrastNumber.value = contrastValue;
  218. setFilterOnElement(htmlElement);
  219. if (fullscreenElement) {
  220. setFilterOnElement(fullscreenElement);
  221. }
  222. };
  223. reader.readAsText(file);
  224. }
  225. });
  226. }
  227.  
  228. createControlPanel();
  229.  
  230. // Applying filters to full screen elements like video
  231. let fullscreenElement = null;
  232. document.onfullscreenchange = () => {
  233. let currentFullScreenElement = document.fullscreenElement;
  234. if (currentFullScreenElement) {
  235. if (currentFullScreenElement.tagName == "HTML") {
  236. return;
  237. }
  238. fullscreenElement = currentFullScreenElement;
  239. setFilterOnElement(fullscreenElement);
  240. } else {
  241. if (fullscreenElement) {
  242. fullscreenElement.style.filter = "none";
  243. fullscreenElement = null;
  244. }
  245. }
  246. };
  247.  
  248. // Listen for storage changes
  249. window.addEventListener('storage', function(e) {
  250. if (e.key === 'defaultEnhancerActive') {
  251. defaultEnhancerActive = GM_getValue('defaultEnhancerActive', false);
  252. setFilterOnElement(htmlElement);
  253. }
  254. });
  255.  
  256. // Keyboard shortcut
  257. document.addEventListener('keydown', (e) => {
  258. if (e.key === 'E' && e.ctrlKey) {
  259. toggleEnhancer();
  260. }
  261. });
  262.  
  263. // Apply filter on initial load
  264. setFilterOnElement(htmlElement);
  265. })();