Reputation: 62
//represents clicked buttons (to turn on and change subtitle)
const settinsgBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-settings-button.ytp-hd-quality-badge");
const translateBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-subtitles-button.ytp-button");
const langueContentBtn = document.querySelectorAll(".ytp-menuitem-content")[2];
const autoTranslateBtn = document.querySelectorAll(".ytp-menuitem-label")[2];
const myLangueBtn = document.querySelectorAll('.ytp-menuitem-label')[80]
//It checks every second whether the buttons are on the screen and clicks the button if there is.
//Each button makes another button appear on the screen.
const checkElements = setInterval(clicker, 1000);
let count = 1;
function clicker() {
if (count === 1 && translateBtn) {
translateBtn.click(); count++;
}
else if (count === 2 && settinsgBtn) {
settinsgBtn.click(); count++;
}
else if (count === 3 && langueContentBtn) {
langueContentBtn.click(); count++;
}
else if (count === 4 && autoTranslateBtn) {
autoTranslateBtn.click(); count++;
}
else if (count === 5 && myLangueBtn) {
myLangueBtn.click(); count = 0;
clearInterval(checkElements);
}
}
clicker();
I want the youtube video to press the subtitle button and translate it into my language from the settings, but the variables are not defined immediately. If the button to be clicked is not visible at first, the variable returns undefined value. It works when I redefine the variable after the button appears. I tried without defining the variable but it again doesn't work after the 2nd click. And MutationObserver
method is very complex, maybe you have a method.
Upvotes: 3
Views: 779
Reputation: 3439
You can resolve this by using MutationObserver, that can notify about changes of the DOM:
function changeSubtitleLanguage(language) {
let steps = 0;
const subtitleBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-subtitles-button");
const settingBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-settings-button");
// helper function:
// select by css selector,
// or by selector & text
function selectItem(node, selector, text) {
if (text) {
return Array.from(node.querySelectorAll(selector))
.find(el => el.innerText.includes(text));
} else {
return node.querySelector(selector);
}
}
const observer = new MutationObserver((records) => {
const subtitleChosen = selectItem(document, '.ytp-subtitles-button.ytp-button[aria-pressed="true"]');
// check if subtitle chosen
if (subtitleChosen && steps === 0) {
steps += 1;
settingBtn.click();
}
const subtitleMenuItem = selectItem(document, '.ytp-menuitem-label span', 'Subtitles/CC');
// check if subtitle item is
// available in menu panel
if (subtitleMenuItem && steps === 1) {
steps += 1;
subtitleMenuItem.click();
}
const autoTranslateMenuItem = selectItem(document, '.ytp-menuitem-label', 'Auto-translate');
// check if autotranslate item is
// available in menu panel
if (autoTranslateMenuItem && steps === 2) {
steps += 1;
autoTranslateMenuItem.click();
}
const languageMenuItem = selectItem(document, '.ytp-menuitem-label', language);
// check if language item is
// available in menu panel
if (languageMenuItem && steps === 3) {
// stop observer,
// when accomplished
observer.disconnect();
languageMenuItem.click();
}
})
observer.observe(
// observe parent of .ytp-settings-menu
selectItem(document, '.html5-video-player'),
{
childList: true,
subtree: true
}
);
if (subtitleBtn && settingBtn &&
!selectItem(document, '.ytp-subtitles-button.ytp-button[aria-pressed="true"]')
) {
// if necessary, the 'subtitleBtn'
// can also be put inside
// an observer callback
subtitleBtn.click();
}
}
changeSubtitleLanguage('Bulgarian');
Upvotes: 1
Reputation: 195992
If the elements are not initially loaded, and they get added after each action, you should re-query for the elements.
Here is a solution where you query the document each time before doing the checks.
You also do not need to manually call the clicker
at the end, since you have fired an interval already.
//represents clicked buttons (to turn on and change subtitle)
let settinsgBtn;
let translateBtn;
let langueContentBtn;
let autoTranslateBtn;
let myLangueBtn;
function getElements() {
settinsgBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-button.ytp-settings-button.ytp-hd-quality-badge");
translateBtn = document.querySelector("#movie_player > div.ytp-chrome-bottom > div.ytp-chrome-controls > div.ytp-right-controls > button.ytp-subtitles-button.ytp-button");
langueContentBtn = document.querySelectorAll(".ytp-menuitem-content")[2];
autoTranslateBtn = document.querySelectorAll(".ytp-menuitem-label")[2];
myLangueBtn = document.querySelectorAll('.ytp-menuitem-label')[80]
}
//It checks every second whether the buttons are on the screen and clicks the button if there is.
//Each button makes another button appear on the screen.
const checkElements = setInterval(clicker, 1000);
let count = 1;
function clicker() {
getElements();
if (count === 1 && translateBtn) {
translateBtn.click();
count++;
} else if (count === 2 && settinsgBtn) {
settinsgBtn.click();
count++;
} else if (count === 3 && langueContentBtn) {
langueContentBtn.click();
count++;
} else if (count === 4 && autoTranslateBtn) {
autoTranslateBtn.click();
count++;
} else if (count === 5 && myLangueBtn) {
myLangueBtn.click();
count = 0;
clearInterval(checkElements);
}
}
A more flexible approach that work with the actual labels of the buttons would be something like
function setSubtitlesToLanguage(languageText = 'English', fallbackToAutotranslate = false) {
const settingsButton = document.querySelector('button.ytp-settings-button');
const subtitlesButton = document.querySelector('button.ytp-subtitles-button');
if (!settingsButton || !subtitlesButton) {
setTimeout(setSubtitlesToLanguage, 500, languageText, fallbackToAutogenerated);
return;
}
// enable subtitles
if (subtitlesButton.getAttribute('aria-pressed') === 'false') {
subtitlesButton.click();
}
// open settings
settingsButton.click();
const settingOptions = [...document.querySelectorAll('.ytp-settings-menu .ytp-panel .ytp-menuitem')];
const subtitlesOption = settingOptions.find(node => node.textContent.includes('Subtitles/CC'));
// open subtitles menu
subtitlesOption.click();
const subtitles = [...document.querySelectorAll('.ytp-settings-menu .ytp-panel .ytp-menuitem')];
const targetSubtitle = subtitles.find(node => node.textContent.includes(languageText));
if (targetSubtitle) {
targetSubtitle.click();
return;
}
if (fallbackToAutotranslate) {
const autoTranslate = subtitles.find(node => node.textContent.includes('Auto-translate'));
if (autoTranslate) {
autoTranslate.click();
setAutoTranslateLanguage(languageText);
}
}
}
function setAutoTranslateLanguage(languageText) {
const autoSubstitles = [...document.querySelectorAll('.ytp-settings-menu .ytp-panel .ytp-menuitem')];
if (!autoSubstitles.length) {
setTimeout(setAutoTranslateLanguage, 500, languageText);
}
const targetAutoSubtitle = autoSubstitles.find(node => node.textContent.includes(languageText));
if (targetAutoSubtitle) {
console.log('starting auto-translate');
targetAutoSubtitle.click();
}
}
// usage
setSubtitlesToLanguage('Greek', true);
Upvotes: 1