您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically sets YouTube videos to their original audio language
// ==UserScript== // @name YouTube Original Audio Selector // @namespace http://tampermonkey.net/ // @version 1.3 // @description Automatically sets YouTube videos to their original audio language // @author You // @match https://www.youtube.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Debug logging function function debugLog(message, element = null) { console.log(`[YT Audio Debug] ${message}`, element || ''); } // Keep track of our observers and timeouts to clean them up let urlObserver = null; let activeTimeouts = []; let checkInterval = null; // Function to clear all pending timeouts function clearAllTimeouts() { activeTimeouts.forEach(timeout => clearTimeout(timeout)); activeTimeouts = []; if (checkInterval) { clearInterval(checkInterval); checkInterval = null; } } // Function to safely set a timeout that we can clean up later function safeSetTimeout(callback, delay) { const timeoutId = setTimeout(() => { const index = activeTimeouts.indexOf(timeoutId); if (index > -1) { activeTimeouts.splice(index, 1); } callback(); }, delay); activeTimeouts.push(timeoutId); return timeoutId; } // Function to check if video has multiple audio tracks function hasMultipleAudioTracks() { const autoGeneratedText = document.evaluate( "//*[contains(text(),'Audio tracks for some languages were automatically generated')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; return !!autoGeneratedText; } // Function to identify the original audio track from menu items function findOriginalAudioTrack(menuItems) { debugLog('Analyzing available audio tracks...'); // Convert NodeList to Array and map to objects with text and analysis const tracks = Array.from(menuItems).map(item => { const text = item.textContent.toLowerCase(); return { element: item, text: text, isAutoGenerated: text.includes('auto-generated'), hasOriginalMarker: text.includes('original'), // Some videos mark the original with a star or different formatting hasSpecialMarker: /[★✓]/.test(item.textContent) }; }); debugLog('Found tracks:', tracks.map(t => t.text)); // First priority: Track explicitly marked as "original" const originalTrack = tracks.find(t => t.hasOriginalMarker); if (originalTrack) { debugLog('Found track marked as original'); return originalTrack.element; } // Second priority: Track with special marking and not auto-generated const specialTrack = tracks.find(t => t.hasSpecialMarker && !t.isAutoGenerated); if (specialTrack) { debugLog('Found track with special marking'); return specialTrack.element; } // Third priority: First track that isn't marked as auto-generated const nonAutoTrack = tracks.find(t => !t.isAutoGenerated); if (nonAutoTrack) { debugLog('Found non-auto-generated track'); return nonAutoTrack.element; } debugLog('No suitable original track found'); return null; } // Function to remove lingering tooltips function removeTooltips() { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, which: 27, bubbles: true, cancelable: true })); } // Function to clean up all resources function cleanup() { if (urlObserver) { urlObserver.disconnect(); urlObserver = null; } clearAllTimeouts(); debugLog('Cleaned up all resources'); } // Function to find and click the original audio option function setOriginalAudio() { debugLog('Starting audio selection process...'); // Check if we need to do anything if (!hasMultipleAudioTracks()) { debugLog('No multiple audio tracks detected, skipping'); cleanup(); return; } // First, find the settings button (gear icon) const settingsButton = document.querySelector('.ytp-settings-button'); if (!settingsButton) { debugLog('Settings button not found'); cleanup(); return; } debugLog('Found settings button, clicking...'); settingsButton.click(); // After clicking settings, wait for menu and look for Audio track option safeSetTimeout(() => { const menuItems = document.querySelectorAll('.ytp-menuitem'); debugLog('Looking for audio track option...'); // Look specifically for the Audio track option const audioTrackItem = Array.from(menuItems).find(item => { const label = item.querySelector('.ytp-menuitem-label'); return label && label.textContent.includes('Audio track'); }); if (!audioTrackItem) { debugLog('No audio track menu item found, closing menu'); settingsButton.click(); removeTooltips(); cleanup(); return; } debugLog('Found audio track item, clicking...'); audioTrackItem.click(); // Wait for submenu and select original language safeSetTimeout(() => { const submenuItems = document.querySelectorAll('.ytp-menuitem'); const originalAudio = findOriginalAudioTrack(submenuItems); if (originalAudio) { debugLog('Found original audio option, selecting...'); originalAudio.click(); // Wait a bit and close the settings menu safeSetTimeout(() => { settingsButton.click(); removeTooltips(); cleanup(); }, 100); } else { debugLog('Original audio option not found'); settingsButton.click(); removeTooltips(); cleanup(); } }, 300); }, 300); } // Function to handle navigation changes function handleNavigation() { debugLog('Navigation detected, waiting for video player...'); clearAllTimeouts(); safeSetTimeout(() => { setOriginalAudio(); }, 1500); } // Start observing URL changes function startObserving() { if (urlObserver) { debugLog('Cleaning up previous observer before starting new one'); cleanup(); } let lastUrl = location.href; urlObserver = new MutationObserver(() => { const url = location.href; if (url !== lastUrl) { lastUrl = url; debugLog('URL changed to:', url); handleNavigation(); } }); urlObserver.observe(document, {subtree: true, childList: true}); } // Add a manual trigger for testing window.debugYTAudio = () => { debugLog('Manual trigger activated'); setOriginalAudio(); }; // Initial setup startObserving(); handleNavigation(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址