YouTube Original Audio Priority

Uses a robust, event-driven model to reliably select the "Original" audio track over dubbed versions.

// ==UserScript==
// @name         YouTube Original Audio Priority
// @namespace    tom.heek
// @version      1.0
// @description  Uses a robust, event-driven model to reliably select the "Original" audio track over dubbed versions.
// @author       Tom Heek
// @match        https://www.youtube.com/*
// @match        https://www.youtube-nocookie.com/*
// @icon         https://www.youtube.com/favicon.ico
// @grant        none
// @run-at       document-start
// @license      CC-BY-NC-ND-4.0
// ==/UserScript==

/*
    This work is licensed under the
    Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
    To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/
    or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

    You are free to use and share this script in its original, unmodified form
    for non-commercial purposes only, provided you give attribution to the original author.
    You may not distribute any modified versions of this script.
*/

"use strict";

let mainObserver = null;
let hasAttemptedSwitch = false;

/**
 * Initializes an observer to find the YouTube player and attach event listeners.
 * This runs on initial load and on each subsequent navigation.
 */
function init() {
    // On SPA navigation, disconnect the old observer to prevent duplicates.
    if (mainObserver) {
        mainObserver.disconnect();
    }
    hasAttemptedSwitch = false;

    mainObserver = new MutationObserver((mutations, obs) => {
        const player = document.getElementById("movie_player");

        if (player && typeof player.addEventListener === 'function' && !player.audioListenerAttached) {
            obs.disconnect();

            // Hook into the player's state changes to know when a video is playing.
            player.addEventListener("onStateChange", handlePlayerStateChange);
            player.audioListenerAttached = true; // Flag to prevent attaching multiple listeners.

            // Handle case where the script loads after the video has already started.
            if (player.getPlayerState && player.getPlayerState() === 1) {
                handlePlayerStateChange(1);
            }
        }
    });

    mainObserver.observe(document.body, { childList: true, subtree: true });
}

/**
 * Callback for the player's 'onStateChange' event.
 * @param {number} state - The new player state (1 means 'Playing').
 */
function handlePlayerStateChange(state) {
    // Run once when the video starts playing.
    if (state === 1 && !hasAttemptedSwitch) {
        hasAttemptedSwitch = true;
        const player = document.getElementById("movie_player");
        if (player) {
            // A minimal delay ensures metadata is stable before access.
            setTimeout(() => forceOriginalAudioTrack(player), 100);
        }
    }
}

/**
 * Checks available audio tracks and switches to the original.
 * @param {object} player - The YouTube player instance.
 */
function forceOriginalAudioTrack(player) {
    try {
        if (typeof player.getAvailableAudioTracks !== 'function') return;

        const availableTracks = player.getAvailableAudioTracks();
        const currentTrack = player.getAudioTrack();

        if (!availableTracks || availableTracks.length <= 1) {
            return;
        }

        let targetTrack = null;

        // Primary Method: Find the track officially marked as original.
        targetTrack = availableTracks.find(track => track.isOriginalTrack === true);

        // Fallback Method: If no official track is found, search by name.
        if (!targetTrack) {
            targetTrack = availableTracks.find(track =>
                track.displayName?.toLowerCase().includes("original")
            );
        }

        // Switch to the target track if it was found and is not already active.
        if (targetTrack && (!currentTrack || currentTrack.id !== targetTrack.id)) {
            player.setAudioTrack(targetTrack);
            console.log(`%c[YouTube Original Audio Priority] Switched to track: '${targetTrack.displayName}'`, 'color: #1E90FF; font-weight: bold;');
        }
    } catch (error) {
        console.error("[YouTube Original Audio Priority] Error during track switch:", error);
    }
}

// --- Script Execution ---

// YouTube is a Single Page Application, so we must re-initialize on navigation.
window.addEventListener("yt-navigate-finish", init);

// Initial run for the first page load.
init();