Greasy Fork

Greasy Fork is available in English.

YouTube 防止自动配音

强制所有 YouTube 视频以原始语言音频播放,而不是 AI 配音的语言。适用于一般视频和 Shorts。可针对单个视频手动关闭。

// ==UserScript==
// @name                           YouTube Prevent Auto-Dub
// @name:zh-TW                     YouTube 防止自動配音
// @name:zh-CN                     YouTube 防止自动配音
// @name:ja                        YouTube 自動吹き替え防止
// @icon                           https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @author                         ElectroKnight22
// @namespace                      electroknight22_prevent_auto_dub_namespace
// @version                        0.1.0
// @match                          *://www.youtube.com/*
// @match                          *://m.youtube.com/*
// @match                          *://www.youtube-nocookie.com/*
// @exclude                        *://www.youtube.com/live_chat*
// @run-at                         document-start
// @license                        MIT
// @description                    Forces all YouTube videos to play in their original language audio instead of the AI dubbed language. Works in both normal videos and shorts. Can be manually overridden per-video.
// @description:zh-TW              強制所有 YouTube 影片以原始語言音訊播放,而不是 AI 配音的語言。適用於一般影片和 Shorts。可針對單一影片手動關閉。
// @description:zh-CN              强制所有 YouTube 视频以原始语言音频播放,而不是 AI 配音的语言。适用于一般视频和 Shorts。可针对单个视频手动关闭。
// @description:ja                 すべてのYouTube動画をAI吹き替えではなく、元の言語の音声で強制的に再生します。通常の動画とショート動画の両方で機能します。動画ごとに手動で無効にすることもできます。
// ==/UserScript==

/*jshint esversion: 11 */

(function () {
    'use strict';

    function fallbackGetPlayer() {
        if (window.location.pathname.startsWith('/shorts')) {
            return document.querySelector('#shorts-player');
        } else if (window.location.pathname.startsWith('/watch')) {
            return document.querySelector('#movie_player');
        } else {
            return document.querySelector('.inline-preview-player');
        }
    }

    function main(event) {
        try {
            const getTrackId = (track) => Object.values(track ?? {}).find((p) => p?.id)?.id ?? null;
            const player = event?.target?.player_ ?? fallbackGetPlayer();
            const availableTracks = player.getAvailableAudioTracks();
            if (availableTracks.length === 0) throw new Error('Unable to determine available audio tracks.');
            const renderer = player.getPlayerResponse()?.captions?.playerCaptionsTracklistRenderer;
            const originalAudioId = renderer?.audioTracks?.[renderer?.defaultAudioTrackIndex]?.audioTrackId;
            if (!originalAudioId || getTrackId(player.getAudioTrack()) === originalAudioId) return; // No undo necessary so return early.
            console.log('Auto-dub detected, trying to undo...');
            const dubAudioTrack = player.getAudioTrack();
            const originalAudioTrack = availableTracks.find((track) => getTrackId(track) === originalAudioId);
            if (!originalAudioTrack) throw new Error('Unable to determine the original audio track.');
            player.setAudioTrack(originalAudioTrack);
            console.log(`Auto-dub undo successful. Audio track reverted from ${dubAudioTrack} to ${originalAudioTrack}.`);
        } catch (error) {
            console.error('Failed to prevent YouTube auto-dubbing.', error);
        }
    }

    const playerUpdateEvent = window.location.hostname === 'm.youtube.com' ? 'state-navigateend' : 'yt-player-updated';
    window.addEventListener(playerUpdateEvent, main, true);
})();