YouTube Audiotrack Reset

Overrides automatic use of generated, translated audiotracks on YouTube videos. Resets to original audio.

  1. // ==UserScript==
  2. // @name YouTube Audiotrack Reset
  3. // @version 0.1.4
  4. // @description Overrides automatic use of generated, translated audiotracks on YouTube videos. Resets to original audio.
  5. // @author PolyMegos (https://github.com/polymegos)
  6. // @namespace https://github.com/polymegos/yt-original-audiotrack/
  7. // @supportURL https://github.com/polymegos/yt-original-audiotrack/issues
  8. // @license MIT
  9. // @match *://www.youtube.com/*
  10. // @match *://www.youtube-nocookie.com/*
  11. // @match *://m.youtube.com/*
  12. // @match *://music.youtube.com/*
  13. // @grant none
  14. // @run-at document-start
  15. // @compatible firefox
  16. // @compatible edge
  17. // @compatible safari
  18. // ==/UserScript==
  19.  
  20. (function() {
  21. 'use strict';
  22.  
  23. function redirectToDesktop() {
  24. // Check if we're on m.youtube.com or in a mobile setting
  25. const isMobile = window.location.hostname === 'm.youtube.com' ||
  26. (window.location.hostname === 'www.youtube.com' &&
  27. (document.documentElement.classList.contains('mobile')));
  28. // Look whether desktop param already in URL
  29. const hasDesktopParam = window.location.search.includes('app=desktop');
  30. if (isMobile && !hasDesktopParam) {
  31. // Appending desktop parameter for new URL
  32. let newUrl = window.location.href;
  33. if (newUrl.includes('?')) {
  34. newUrl += '&app=desktop';
  35. } else {
  36. newUrl += '?app=desktop';
  37. }
  38. // Redirect to desktop version
  39. console.log('Redirecting to desktop version of YouTube...');
  40. window.location.href = newUrl;
  41. return true; // redirect
  42. }
  43. return false; // no redirect needed
  44. }
  45.  
  46. // Wait for an element to appear in the DOM
  47. function waitForElement(selector, timeout = 10000) {
  48. return new Promise((resolve, reject) => {
  49. const element = document.querySelector(selector);
  50. if (element) return resolve(element);
  51. const observer = new MutationObserver((mutations, obs) => {
  52. const target = document.querySelector(selector);
  53. if (target) {
  54. obs.disconnect();
  55. resolve(target);
  56. }
  57. });
  58. observer.observe(document.body, { childList: true, subtree: true });
  59. setTimeout(() => {
  60. observer.disconnect();
  61. reject(new Error(`Timeout: Element ${selector} not found within ${timeout}ms`));
  62. }, timeout);
  63. });
  64. }
  65.  
  66. // Simulate a click on the given element
  67. function clickElement(element) {
  68. if (element) {
  69. element.click();
  70. }
  71. }
  72.  
  73. // Wait until no ad shown
  74. function waitForNoAds(timeout = 10000) {
  75. return new Promise((resolve, reject) => {
  76. const player = document.querySelector('.html5-video-player');
  77. if (!player || !player.classList.contains('ad-showing')) return resolve();
  78. const observer = new MutationObserver((mutations, obs) => {
  79. if (!player.classList.contains('ad-showing')) {
  80. obs.disconnect();
  81. resolve();
  82. }
  83. });
  84. observer.observe(player, { attributes: true, attributeFilter: ['class'] });
  85. setTimeout(() => {
  86. observer.disconnect();
  87. reject(new Error('Timeout: Ad still showing.'));
  88. }, timeout);
  89. });
  90. }
  91.  
  92. // Main function to reset the audiotrack
  93. async function checkAudiotrack() {
  94. try {
  95. if (redirectToDesktop()) {
  96. return; // Early return to redirect to desktop view
  97. }
  98.  
  99. // Wait for the video element and ensure no ad is playing
  100. await waitForElement('video');
  101. await waitForNoAds();
  102.  
  103. // Open the settings menu
  104. const settingsButton = await waitForElement('.ytp-settings-button');
  105. clickElement(settingsButton);
  106. const settingsMenu = await waitForElement('.ytp-popup.ytp-settings-menu');
  107.  
  108. // Find and click the "Audiotrack" item
  109. const audioTrackItem = Array.from(settingsMenu.querySelectorAll('.ytp-menuitem'))
  110. .find(item => item.textContent.includes('Audiotrack'));
  111.  
  112. if (audioTrackItem) {
  113. clickElement(audioTrackItem);
  114.  
  115. // Wait for the audiotrack submenu to appear
  116. const audioTrackMenu = await waitForElement('.ytp-popup.ytp-settings-menu');
  117.  
  118. // Click the "Original" option
  119. const originalOption = Array.from(audioTrackMenu.querySelectorAll('.ytp-menuitem'))
  120. .find(item => item.textContent.toLowerCase().includes('original'));
  121.  
  122. if (originalOption) {
  123. clickElement(originalOption);
  124. } else {
  125. console.warn('"Original" audiotrack not found.');
  126. }
  127. // Close settings menu
  128. clickElement(settingsButton);
  129. } else {
  130. console.warn('Audiotrack menu not found.');
  131. // Close half-open settings menu
  132. clickElement(settingsButton);
  133. }
  134. } catch (error) {
  135. console.error('Error in script:', error);
  136. }
  137. }
  138.  
  139. // Initial trigger on page load
  140. checkAudiotrack();
  141.  
  142. // Re-run the script after SPA navigation events (when switching videos)
  143. document.addEventListener('yt-navigate-finish', () => {
  144. checkAudiotrack();
  145. });
  146. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址