YouTube Sharpen Always On

Sharpening filter for YouTube

当前为 2025-06-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Sharpen Always On
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Sharpening filter for YouTube
  6. // @author ChatGPT
  7. // @match https://www.youtube.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (() => {
  12. const FILTER_ID = 'tm-sharpen-filter';
  13. const CSS_CLASS = 'tm-sharpen-enabled';
  14. const BUTTON_ID = 'tm-sharpen-button';
  15. let filterEnabled = true;
  16. let currentVideo = null;
  17.  
  18. function injectFilterAndStyles() {
  19. if (document.getElementById(FILTER_ID)) return;
  20.  
  21. const container = document.createElement('div');
  22. container.style.position = 'absolute';
  23. container.style.width = '0';
  24. container.style.height = '0';
  25. container.style.overflow = 'hidden';
  26. container.innerHTML = `
  27. <svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  28. <filter id="${FILTER_ID}">
  29. <feConvolveMatrix order="3" kernelMatrix="0 -0.125 0 -0.125 1.5 -0.125 0 -0.125 0" divisor="1" bias="0"/>
  30. </filter>
  31. </svg>
  32. <style>
  33. video.${CSS_CLASS} {
  34. filter: url(#${FILTER_ID}) !important;
  35. }
  36. #${BUTTON_ID} {
  37. font-size: 25px;
  38. cursor: pointer;
  39. user-select: none;
  40. position: relative;
  41. top: -16px;
  42. color: red;
  43. background: transparent;
  44. border: none;
  45. opacity: 1;
  46. transition: color 0.3s, opacity 0.3s;
  47. }
  48. #${BUTTON_ID}.active {
  49. color: gray;
  50. opacity: 0.5;
  51. }
  52. </style>
  53. `;
  54. document.body.appendChild(container);
  55. }
  56.  
  57. function toggleFilterButton(btn) {
  58. filterEnabled = !filterEnabled;
  59. btn.classList.toggle('active', filterEnabled);
  60. if (currentVideo) currentVideo.classList.toggle(CSS_CLASS, filterEnabled);
  61. console.log(`[YT Sharpen] ${filterEnabled ? 'Enabled' : 'Disabled'}`);
  62. }
  63.  
  64. function createToggleButton(controls) {
  65. if (document.getElementById(BUTTON_ID)) return;
  66.  
  67. const btn = document.createElement('button');
  68. btn.id = BUTTON_ID;
  69. btn.className = 'ytp-button active';
  70. btn.title = 'Toggle Sharpen Filter';
  71. btn.textContent = '❤';
  72. btn.addEventListener('click', () => toggleFilterButton(btn));
  73. controls.insertBefore(btn, controls.firstChild);
  74. }
  75.  
  76. function observe() {
  77. const observer = new MutationObserver(() => {
  78. const video = document.querySelector('video');
  79. const controls = document.querySelector('.ytp-right-controls');
  80.  
  81. if (video && video !== currentVideo) {
  82. currentVideo = video;
  83. currentVideo.classList.toggle(CSS_CLASS, filterEnabled);
  84. }
  85. if (controls) createToggleButton(controls);
  86. });
  87. observer.observe(document.body, { childList: true, subtree: true });
  88. }
  89.  
  90. function init() {
  91. injectFilterAndStyles();
  92. observe();
  93. }
  94.  
  95. window.addEventListener('load', init, { once: true });
  96. window.addEventListener('yt-navigate-finish', () => setTimeout(init, 500));
  97. })();