// ==UserScript==
// @name Youtube Auto-translate Canceler
// @namespace https://github.com/pcouy/YoutubeAutotranslateCanceler/
// @version 0.1
// @description Remove auto-translated youtube titles
// @author Pierre Couy
// @match https://www.youtube.com/*
// @grant GM_setValue
// @grant GM_getValue
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
/*
Get a YouTube Data v3 API key from https://console.developers.google.com/apis/library/youtube.googleapis.com?q=YoutubeData
*/
var NO_API_KEY = false;
if(GM_getValue("api_key") === undefined || GM_getValue("api_key") === null || GM_getValue("api_key") === ""){
GM_setValue("api_key", prompt("Enter your API key. Go to https://developers.google.com/youtube/v3/getting-started to know how to obtain an API key, then go to https://console.developers.google.com/apis/api/youtube.googleapis.com/ in order to enable Youtube Data API for your key."));
}
if(GM_getValue("api_key") === undefined || GM_getValue("api_key") === null || GM_getValue("api_key") === ""){
NO_API_KEY = true; // Resets after page reload, still allows local title to be replaced
}
const API_KEY = GM_getValue("api_key");
var API_KEY_VALID = false;
var url_template = "https://www.googleapis.com/youtube/v3/videos?part=snippet&id={IDs}&key=" + API_KEY;
var changedTitle;
var changedDescription
var alreadyChanged = [];
function changeTitles(){
// MAIN TITLE
if (!changedTitle && window.location.href.includes ("/watch")){
var videoTitle = document.title.endsWith(" - YouTube")? document.title.substring(0, document.title.length - " - YouTube".length) : null;
var pageTitle = document.getElementsByClassName("title style-scope ytd-video-primary-info-renderer");
if (pageTitle.length > 0 && videoTitle != null && pageTitle[0] !== undefined) {
if (pageTitle[0].innerText != videoTitle){
console.log ("Reverting main video title '" + pageTitle[0].innerText + "' to '" + videoTitle + "'");
pageTitle[0].innerText = videoTitle;
}
changedTitle = true;
}
}
if (NO_API_KEY) {
return;
}
var APIcallIDs;
// REFERENCED VIDEO TITLES
var links = Array.prototype.slice.call(document.getElementsByTagName("a")).filter( a => {
return a.id == 'video-title' && alreadyChanged.indexOf(a) == -1;
} );
var spans = Array.prototype.slice.call(document.getElementsByTagName("span")).filter( a => {
return a.id == 'video-title' && alreadyChanged.indexOf(a) == -1;
} );
links = links.concat(spans).slice(0,30);
// MAIN VIDEO DESCRIPTION
var mainVidID = "";
if (!changedDescription && window.location.href.includes ("/watch")){
mainVidID = window.location.href.split('v=')[1].split('&')[0];
}
if(mainVidID != "" || links.length > 0){
console.log("Checking " + (mainVidID != ""? "main video and " : "") + links.length + " video titles!");
//console.log(links.map(a => a.innerText));
var IDs = links.map( a => {
while(a.tagName != "A"){
a = a.parentNode;
}
var href = a.href;
var tmp = href.split('v=')[1];
return tmp.split('&')[0];
} );
var requestUrl = url_template.replace("{IDs}", (mainVidID != ""? (mainVidID + ",") : "") + IDs.join(','));
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var data = JSON.parse(xhr.responseText);
if(data.kind == "youtube#videoListResponse"){
API_KEY_VALID = true;
data = data.items;
if (mainVidID != "")
{ // Replace Main Video Description
var videoDescription = data[0].snippet.description;
var pageDescription = document.getElementsByClassName("content style-scope ytd-video-secondary-info-renderer");
if (pageDescription.length > 0 && videoDescription != null && pageDescription[0] !== undefined) {
// TODO: linkify replaces links correctly for now, but without redirect or other specific youtube stuff.
// Works, but need to verify it works since this replaces ALL descriptions, even if it was not translated in the first place (no easy comparision possible)
pageDescription[0].innerHTML = linkify(videoDescription);
console.log ("Reverting main video description!");
changedDescription = true;
}
else console.log ("Failed to find main video description!");
}
var titleStore = {}
data = data.forEach( v => {
titleStore[v.id] = v.snippet.title;
} );
//console.log("Original Titles (API):");
//console.log(titleStore);
for(var i=0 ; i < links.length ; i++){
var originalTitle = titleStore[IDs[i]];
if(titleStore[IDs[i]] !== undefined && links[i].innerText != originalTitle.replace(/\s{2,}/g, ' ')){
console.log ("'" + links[i].innerText + "' --> '" + originalTitle + "'");
links[i].innerText = originalTitle;
}
alreadyChanged.push(links[i]);
}
} else {
console.log("API Request Failed!");
console.log(requestUrl);
console.log(data);
// This ensures that occasional fails don't stall the script
// But if the first query is a fail then it won't try repeatedly
NO_API_KEY = !API_KEY_VALID;
if (NO_API_KEY) {
console.log("API Key Fail! Please Reload!");
}
}
}
};
xhr.open('GET', requestUrl);
xhr.send();
}
}
function linkify(inputText) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
//URLs starting with http://, https://, or ftp://
replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '$1');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '$1');
return replacedText;
}
// Execute every seconds in case new content has been added to the page
// DOM listener would be good if it was not for the fact that Youtube changes its DOM frequently
setInterval(changeTitles, 1000);
})();