Threads-Volume

设定 Threads 影片的预设音量

  1. // ==UserScript==
  2. // @name Threads-Volume
  3. // @namespace threadsVolume
  4. // @version 1.2.0
  5. // @description Set your Threads videos default volumes
  6. // @description:zh-TW 設定 Threads 影片的預設音量
  7. // @description:zh-CN 设定 Threads 影片的预设音量
  8. // @author Lin_tsen
  9. // @match *://*.threads.com/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=threads.net
  11. // @grant none
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. console.log("threadsVolumeCustomize");
  16.  
  17. window.addEventListener('load', () => {
  18. if (!localStorage.getItem('defaultVolume')) {
  19. localStorage.setItem('defaultVolume', 0.2);
  20. }
  21.  
  22. const findVolumeDiv = () => {
  23. const targetElement = document.querySelector('header') || document.body;
  24. if (!targetElement) return;
  25.  
  26. const volumeDiv = document.createElement('div');
  27. volumeDiv.id = 'volumeDiv';
  28. volumeDiv.style.display = 'flex';
  29. volumeDiv.style.alignItems = 'center';
  30. volumeDiv.style.justifyContent = 'space-between';
  31. volumeDiv.style.padding = '8px 12px';
  32. volumeDiv.style.borderRadius = '8px';
  33. volumeDiv.style.cursor = 'pointer';
  34. volumeDiv.style.position = 'fixed';
  35. volumeDiv.style.top = '10px';
  36. volumeDiv.style.right = '10px';
  37. volumeDiv.style.zIndex = '9999';
  38. volumeDiv.style.backgroundColor = 'rgba(0,0,0,0.5)';
  39. volumeDiv.style.maxHeight = '3vh';
  40. volumeDiv.style.color = 'white'; // 數字白色,無背景
  41.  
  42. const volumeTextContainer = document.createElement('div');
  43. volumeTextContainer.style.overflow = 'hidden';
  44.  
  45. const volumeText = document.createElement('span');
  46. volumeText.id = 'volumeText';
  47. volumeText.style.paddingLeft = '10px';
  48. volumeText.textContent = 'Volume';
  49.  
  50. const volumeSelectorContainer = document.createElement('div');
  51. volumeSelectorContainer.style.display = 'flex';
  52. volumeSelectorContainer.style.alignItems = 'center';
  53.  
  54. const volumeSelectorText = document.createElement('span');
  55. volumeSelectorText.textContent = Math.round(localStorage.getItem('defaultVolume') * 100 || 20); // 顯示整數
  56. volumeSelectorText.style.marginLeft = '10px';
  57. volumeSelectorText.style.fontSize = '14px';
  58. volumeSelectorText.style.minWidth = '30px'; // 保持寬度一致
  59. volumeSelectorText.style.textAlign = 'center';
  60.  
  61. const volumeSelectorInput = document.createElement('input');
  62. volumeSelectorInput.type = 'range';
  63. volumeSelectorInput.value = localStorage.getItem('defaultVolume') * 100 || 20;
  64. volumeSelectorInput.min = 0;
  65. volumeSelectorInput.max = 100;
  66. volumeSelectorInput.step = 1; // 只顯示整數
  67. volumeSelectorInput.style.display = 'none';
  68. volumeSelectorInput.style.cursor = 'ew-resize';
  69. volumeSelectorInput.style.width = '100px'; // 滑桿寬度
  70.  
  71. volumeSelectorInput.addEventListener('input', () => {
  72. let volumeValue = Math.round(volumeSelectorInput.value); // 取整數
  73. volumeSelectorText.textContent = volumeValue;
  74. localStorage.setItem('defaultVolume', volumeValue / 100);
  75. });
  76.  
  77. // hover 效果
  78. volumeDiv.addEventListener('mouseenter', () => {
  79. volumeDiv.style.backgroundColor = '#1A1A1A';
  80. volumeText.style.color = 'white';
  81. });
  82. volumeDiv.addEventListener('mouseleave', () => {
  83. volumeDiv.style.backgroundColor = 'rgba(0,0,0,0.5)';
  84. volumeText.style.color = '';
  85. });
  86.  
  87. let showVolumeSlider = false;
  88. volumeDiv.addEventListener('click', (event) => {
  89. event.stopPropagation();
  90. showVolumeSlider = !showVolumeSlider;
  91. volumeSelectorInput.style.display = showVolumeSlider ? 'block' : 'none';
  92. });
  93.  
  94. volumeSelectorContainer.appendChild(volumeSelectorText);
  95. volumeSelectorContainer.appendChild(volumeSelectorInput);
  96.  
  97. volumeDiv.appendChild(volumeTextContainer);
  98. volumeDiv.appendChild(volumeSelectorContainer);
  99. volumeTextContainer.appendChild(volumeText);
  100.  
  101. targetElement.appendChild(volumeDiv);
  102. };
  103.  
  104. setInterval(() => {
  105. if (!document.getElementById('volumeDiv')) {
  106. findVolumeDiv();
  107. }
  108. }, 1000);
  109.  
  110. const setVolumeForVideos = () => {
  111. const defaultVolume = parseFloat(localStorage.getItem('defaultVolume'));
  112. const videos = document.getElementsByTagName('video');
  113. for (let i = 0; i < videos.length; i++) {
  114. videos[i].volume = defaultVolume;
  115. }
  116. };
  117.  
  118. setVolumeForVideos();
  119.  
  120. new MutationObserver(() => {
  121. setVolumeForVideos();
  122. }).observe(document.body, { childList: true, subtree: true });
  123.  
  124. });
  125.  
  126. // / \----------------,
  127. // \_,| |
  128. // | Lin_tsen |
  129. // | ,--------------
  130. // \_/_____________/