Skip to content

Instantly share code, notes, and snippets.

@carcigenicate
Created February 26, 2023 23:06
Show Gist options
  • Select an option

  • Save carcigenicate/71e044332421649c51d48a17f2c97038 to your computer and use it in GitHub Desktop.

Select an option

Save carcigenicate/71e044332421649c51d48a17f2c97038 to your computer and use it in GitHub Desktop.
YouTube Subtitle Language Changer
// ==UserScript==
// @name Subtitle Language Changer
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Spawns a small textbox and button to easily allow changing YouTube's subtitle language.
// @author You
// @match https://www.youtube.com/watch*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
const SETTINGS_BTN_PATH = '#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-settings-button';
const SUBTITLES_BTN_PATH = '#ytp-id-18 > div > div > div:nth-child(4) > div.ytp-menuitem-label > div > span:nth-child(1)';
const ENGLISH_BTN_PATH = '#ytp-id-18 > div > div.ytp-panel-menu > div:nth-child(2) > div';
const LANGUAGE_SELECT_BTN_PATH = '#ytp-id-18 > div > div.ytp-panel-menu > div:nth-child(3) > div';
const LANGUAGE_CONTAINER_PATH = '#ytp-id-18 > div > div.ytp-panel-menu'; // > div:nth-child(5) > div
function clickInOrder(selectors, delay) {
return new Promise((resolve) => {
const selectorCopy = [...selectors];
function loop() {
if (selectorCopy.length > 0) {
const elem = document.querySelector(selectorCopy.splice(0, 1)[0]);
console.log("CLICKING", elem);
elem.click();
setTimeout(loop, delay);
} else {
resolve();
}
}
loop();
});
}
async function changeSubtitleLanguage(language) {
await clickInOrder([
SETTINGS_BTN_PATH,
SUBTITLES_BTN_PATH,
ENGLISH_BTN_PATH,
SUBTITLES_BTN_PATH,
LANGUAGE_SELECT_BTN_PATH
], 400);
const languagesParent = document.querySelector(LANGUAGE_CONTAINER_PATH);
const loweredSelection = language.toLowerCase();
const languageBtn = [...languagesParent.children].find((child) => child.firstChild.innerHTML.toLowerCase() === loweredSelection);
if (languageBtn) {
languageBtn.click();
} else {
console.error("Could not find language", language);
}
}
const inputHTML = `
<input id='languageSelectInput' />
<button id='languageSelectBtn'>Change</button>
`;
const container = document.createElement('div');
container.innerHTML = inputHTML;
document.body.appendChild(container);
container.style.position = 'absolute';
container.style.top = '4vw';
container.style.left = '2vw';
container.style.zIndex = '99999';
container.style.backgroundColor = 'white';
document.getElementById('languageSelectBtn').onclick = () => {
const input = document.getElementById('languageSelectInput');
changeSubtitleLanguage(input.value);
}
})();
@pawelkurach
Copy link

How are you? I made it tweak: move to bottom, change onkeyup after first letter eg 'e' english 'f' french :)

@pawelkurach
Copy link

`// ==UserScript==
// @name Subtitle Language Changer
// @namespace http://tampermonkey.net/
// @Version 0.1
// @description Spawns a small textbox and button to easily allow changing YouTube's subtitle language.
// @author You
// @match https://www.youtube.com/watch*
// @ICON https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// ==/UserScript==

(function() {
'use strict';

const SETTINGS_BTN_PATH = '#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-settings-button';
const SUBTITLES_BTN_PATH = '#ytp-id-18 > div > div > div:nth-child(4) > div.ytp-menuitem-label > div > span:nth-child(1)';
const ENGLISH_BTN_PATH = '#ytp-id-18 > div > div.ytp-panel-menu > div:nth-child(2) > div';
const LANGUAGE_SELECT_BTN_PATH = '#ytp-id-18 > div > div.ytp-panel-menu > div:nth-child(3) > div';
const LANGUAGE_CONTAINER_PATH = '#ytp-id-18 > div > div.ytp-panel-menu'; // > div:nth-child(5) > div

function clickInOrder(selectors, delay) {
    return new Promise((resolve) => {
        const selectorCopy = [...selectors];
        function loop() {
            if (selectorCopy.length > 0) {
                const elem = document.querySelector(selectorCopy.splice(0, 1)[0]);
                console.log("CLICKING", elem);
                elem.click();
                setTimeout(loop, delay);
            } else {
                resolve();
            }
        }
        loop();
    });

}

async function changeSubtitleLanguage(language) {
    // 1) Open Settings, 2) Open Subtitles/CC
    await clickInOrder([
        SETTINGS_BTN_PATH,
        SUBTITLES_BTN_PATH
    ], 400);

    // 3) Grab and click the correct language
    const parent = document.querySelector(LANGUAGE_CONTAINER_PATH);
    if (!parent) {
        console.error('Subtitle menu not found');
        return;
    }

    const lower = language.toLowerCase().trim();
    let btn;
    if (lower === 'e') {
        btn = [...parent.children].find(c => c.textContent.trim().toLowerCase() === 'english');
    }
    else if (lower === 'f') {
        btn = [...parent.children].find(c =>
                                        ['french','français'].includes(c.textContent.trim().toLowerCase())
                                       );
    }
    else {
        btn = [...parent.children].find(c =>
                                        c.textContent.trim().toLowerCase().startsWith(lower)
                                       );
    }

    if (!btn) {
        console.error('Could not find language for:', language);
        return;
    }
    btn.click();

    // 4) Close Settings menu
    await clickInOrder([ SETTINGS_BTN_PATH ], 200);

    input.focus();
}



const inputHTML = `
    <input id='languageSelectInput' />
    <button id='languageSelectBtn'>Change</button>
`;

// build your UI container once
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.bottom = '4vw'; // move to bottom
container.style.left = '2vw';
container.style.zIndex = '99999';
container.style.backgroundColor = 'white';
container.style.padding = '0.5em';
container.style.border = '1px solid #ccc';
container.style.borderRadius = '4px';
container.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)';

// create the input
const input = document.createElement('input');
input.id = 'languageSelectInput';
input.style.width = '4em';
input.style.marginRight = '0.5em';
input.placeholder = 'e';

// create the button
const button = document.createElement('button');
button.id = 'languageSelectBtn';
button.textContent = 'Change';

// assemble & attach
container.appendChild(input);
container.appendChild(button);
document.body.appendChild(container);

// guard so we only fire once per keystroke
let lastFiredKey = null;

input.addEventListener('keyup', async () => {
    const txt = input.value.trim().toLowerCase();
    // only run if exactly one character, and it’s different from the last one
    if (txt.length !== 1 || txt === lastFiredKey) return;

    lastFiredKey = txt;
    try {
        await changeSubtitleLanguage(txt);
    } catch (e) {
        console.error(e);
    } finally {
        // clear & reset so next key will fire again
        input.value = '';
        lastFiredKey = null;
    }
});

// (optional) keep the button as a fallback
button.addEventListener('click', () => {
    const txt = input.value.trim().toLowerCase();
    if (!txt) return;
    changeSubtitleLanguage(txt).then(() => {
        input.value = '';
    });
});

})();`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment