NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @namespace https://github.com/efroostrf/Youtube-Translate // @name YouTube Translate // @version 1.0.6 // @description Быстрый перевод названия/описания видео на необходимые языки всего в несколько кликов! // @author efroostrf // @copyright 2020-2021, efroostrf (https://openuserjs.org/users/efroostrf) // @match https://studio.youtube.com/* // @require https://code.jquery.com/jquery-3.5.1.min.js // @require https://apis.google.com/js/api.js // @license MIT; https://opensource.org/licenses/MIT // @updateURL https://openuserjs.org/meta/efroostrf/YouTube_Translate.meta.js // @downloadURL https://openuserjs.org/install/efroostrf/YouTube_Translate.user.js // @grant none // ==/UserScript== // ==OpenUserJS== // @author efroostrf // ==/OpenUserJS== (function() { 'use strict'; const APIKey = "AIzaSyAiFKm5Fglp0vON-OhpcBFTnYmx-n4prwg"; const AppCode = '937071706522-dh9rjlst2kh2jsl8q5adu4qv11rd5obj.apps.googleusercontent.com'; const BtnAppend = 'div.footer.style-scope.ytgn-video-translations-section'; const XRapidMicrosoftKey = 'f4fc1397f7mshf3ca60fad5167d5p1b4e2cjsn55d7e3d45bff'; var TranslateBy = 'Microsoft'; var Translated = { "data": [] }; var AccessToken = null; var MyName = null; const Languages = {"translation":{ "af":{"name":"Африкаанс","nativeName":"Afrikaans","dir":"ltr"}, "ar":{"name":"Арабский","nativeName":"العربية","dir":"rtl"}, "as":{"name":"Assamese","nativeName":"Assamese","dir":"ltr"}, "bg":{"name":"Болгарский","nativeName":"Български","dir":"ltr"}, "bn":{"name":"Бенгальский","nativeName":"বাংলা","dir":"ltr"}, "bs":{"name":"Боснийский","nativeName":"bosanski (latinica)","dir":"ltr"}, "ca":{"name":"Каталанский","nativeName":"Català","dir":"ltr"}, "cs":{"name":"Чешский","nativeName":"Čeština","dir":"ltr"}, "cy":{"name":"Валлийский","nativeName":"Welsh","dir":"ltr"}, "da":{"name":"Датский","nativeName":"Dansk","dir":"ltr"}, "de":{"name":"Немецкий","nativeName":"Deutsch","dir":"ltr"}, "el":{"name":"Греческий","nativeName":"Ελληνικά","dir":"ltr"}, "en":{"name":"Английский","nativeName":"English","dir":"ltr"}, "es":{"name":"Испанский","nativeName":"Español","dir":"ltr"}, "et":{"name":"Эстонский","nativeName":"Eesti","dir":"ltr"}, "fa":{"name":"Персидский","nativeName":"Persian","dir":"rtl"}, "fi":{"name":"Финский","nativeName":"Suomi","dir":"ltr"}, "fj":{"name":"фиджи","nativeName":"Fijian","dir":"ltr"}, "fr":{"name":"Французский","nativeName":"Français","dir":"ltr"}, "fr-ca":{"name":"French (Canada)","nativeName":"French (Canada)","dir":"ltr"}, "ga":{"name":"Ирландский","nativeName":"Gaeilge","dir":"ltr"}, "gu":{"name":"Гуджарати","nativeName":"ગુજરાતી","dir":"ltr"}, "he":{"name":"Иврит","nativeName":"עברית","dir":"rtl"}, "hi":{"name":"Хинди","nativeName":"हिंदी","dir":"ltr"}, "hr":{"name":"Хорватский","nativeName":"Hrvatski","dir":"ltr"}, "ht":{"name":"Гаитянский креольский","nativeName":"Haitian Creole","dir":"ltr"}, "hu":{"name":"Венгерский","nativeName":"Magyar","dir":"ltr"}, "id":{"name":"Индонезийский","nativeName":"Indonesia","dir":"ltr"}, "is":{"name":"Исландский","nativeName":"Íslenska","dir":"ltr"}, "it":{"name":"Итальянский","nativeName":"Italiano","dir":"ltr"}, "ja":{"name":"Японский","nativeName":"日本語","dir":"ltr"}, "kk":{"name":"Казахский","nativeName":"Kazakh","dir":"ltr"}, "kn":{"name":"Каннада","nativeName":"ಕನ್ನಡ","dir":"ltr"}, "ko":{"name":"Корейский","nativeName":"한국어","dir":"ltr"}, "ku":{"name":"Kurdish (Central)","nativeName":"Kurdish (Central)","dir":"rtl"}, "lt":{"name":"Литовский","nativeName":"Lietuvių","dir":"ltr"}, "lv":{"name":"Латышский","nativeName":"Latviešu","dir":"ltr"}, "mg":{"name":"малагасийский","nativeName":"Malagasy","dir":"ltr"}, "mi":{"name":"Маори","nativeName":"Māori","dir":"ltr"}, "ml":{"name":"Малаялам","nativeName":"മലയാളം","dir":"ltr"}, "mr":{"name":"Маратхи","nativeName":"मराठी","dir":"ltr"}, "ms":{"name":"Малайский","nativeName":"Melayu","dir":"ltr"}, "mt":{"name":"Мальтийский","nativeName":"Il-Malti","dir":"ltr"}, "nb":{"name":"Норвежский","nativeName":"Norsk","dir":"ltr"}, "nl":{"name":"Голландский","nativeName":"Nederlands","dir":"ltr"}, "or":{"name":"ория","nativeName":"Odia","dir":"ltr"}, "pa":{"name":"Панджаби","nativeName":"ਪੰਜਾਬੀ","dir":"ltr"}, "pl":{"name":"Польский","nativeName":"Polski","dir":"ltr"}, "ps":{"name":"Pashto","nativeName":"Pashto","dir":"rtl"}, "pt":{"name":"Португальский (Бразилия)","nativeName":"Português (Brasil)","dir":"ltr"}, "pt-pt":{"name":"Португальский (Португалия)","nativeName":"Português (Portugal)","dir":"ltr"}, "ro":{"name":"Румынский","nativeName":"Română","dir":"ltr"}, "ru":{"name":"Русский","nativeName":"Русский","dir":"ltr"}, "sk":{"name":"Словацкий","nativeName":"Slovenčina","dir":"ltr"}, "sl":{"name":"Словенский","nativeName":"Slovenščina","dir":"ltr"}, "sm":{"name":"самоа","nativeName":"Samoan","dir":"ltr"}, "sv":{"name":"Шведский","nativeName":"Svenska","dir":"ltr"}, "sw":{"name":"Суахили","nativeName":"Kiswahili","dir":"ltr"}, "ta":{"name":"Тамильский","nativeName":"தமிழ்","dir":"ltr"}, "te":{"name":"Телугу","nativeName":"తెలుగు","dir":"ltr"}, "th":{"name":"Тайский","nativeName":"ไทย","dir":"ltr"}, "to":{"name":"тонга","nativeName":"lea fakatonga","dir":"ltr"}, "tr":{"name":"Турецкий","nativeName":"Türkçe","dir":"ltr"}, "uk":{"name":"Украинский","nativeName":"Українська","dir":"ltr"}, "ur":{"name":"Урду","nativeName":"اردو","dir":"rtl"}, "vi":{"name":"Вьетнамский","nativeName":"Tiếng Việt","dir":"ltr"}, "yue":{"name":"Кантонский (традиционное письмо)","nativeName":"粵語 (繁體中文)","dir":"ltr"}, "zh-Hans":{"name":"Китайский упрощенный","nativeName":"简体中文","dir":"ltr"}, "zh-Hant":{"name":"Китайский традиционный","nativeName":"繁體中文","dir":"ltr"}}}; var loadedLanguages = []; if (localStorage.getItem('YoutubeTranslator') !== undefined && localStorage.getItem('YoutubeTranslator') !== null) { loadedLanguages = JSON.parse(localStorage.getItem('YoutubeTranslator'), true); } $(document).ready(function() { loadCodes(); }); async function awaitAddButton() { var Init = getUrlVars(); if (Init.category == 'translations' && Init.type == 'video') { if ($(".meta-translator-extension").length == 0) { let btn = '<a class="meta-translator-extension">Автоматически добавить перевод</a>'; $(BtnAppend).prepend(btn); $(".meta-translator-extension").on("click", function() { $("#popup-meta-translator-extension").attr({"style":""}); }); } } } function loadCodes() { var adder; function languagesToSelect() { for (var i = 0; i < Object.keys(Languages.translation).length; i++) { let code = Object.keys(Languages.translation)[i]; $(".select-language").append('<option value="'+code+'">'+Languages.translation[code].name+'</option>'); } } async function createElement() { var css = '<style>.meta-translator-extension{position: relative;margin-right:10px;font-weight:500;letter-spacing:0.01em;text-transform: uppercase;margin:24px 0 32px 0;font-size:14px;background:brown;padding:9.5px 15px;border-radius:2px;color:#fff;user-select:none;cursor:pointer}.meta-translator-extension:hover{background:#791d1d}#popup-meta-translator-extension{font-family: var(--paper-font-common-base_-_font-family);position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);z-index:999999999}#popup-meta-translator-extension .content{background:#fff;width:250px;padding:10px 15px}#popup-meta-translator-extension .loading{background:#fff;width:250px;padding:10px 15px}.h{color:#000;font-size:2em}.selected-languages{margin-top:5px;margin-bottom:5px}.selected-languages a{font-size: 13px;margin:2px;display:inline-block;border-radius:5px;background:#e8e8e8;padding:2px 5px;color:#000;user-select:none;cursor:pointer}.infotxt{font-size: 12px;color:#a9a9a9;font-size:1em;word-break:break-word}#g-signin2{margin-top:40px}#Extension-Starts{height:36px;background:brown;color:#fff;font-size:13px;box-shadow:0 2px 4px 0 rgba(0,0,0,.25);text-align:center;margin-left:auto;width:40%;user-select:none;cursor:pointer;-webkit-transition:background-color .218s,border-color .218s,box-shadow .218s;transition:background-color .218s,border-color .218s,box-shadow .218s}.g-signin2{height:36px;background:#fff;color:#000;font-size:13px;box-shadow:0 2px 4px 0 rgba(0,0,0,.25);text-align:center;width:50%;user-select:none;cursor:pointer;-webkit-transition:background-color .218s,border-color .218s,box-shadow .218s;transition:background-color .218s,border-color .218s,box-shadow .218s}#Extension-Starts:hover{-webkit-box-shadow:0 0 3px 3px rgb(244 66 66 / 30%);box-shadow:0 0 3px 3px rgb(244 66 66 / 30%)}.flx{display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flx-column{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.flx-row{-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.flx-center{justify-content:center;align-items:center}</style>'; $('head').append(css); let popup = [ '<div id="popup-meta-translator-extension" class="flx flx-center" style="display: none;">', '<div class="content flx flx-column">', '<div class="selected-languages-h">', '<a class="h">Выбранные языки:</a>', '<div class="selected-languages"></div>', '<a class="infotxt" style="margin-left: 2px;">Для удаление языка из списка кликните на него.</a>', '<a id="DeleteAllLang" class="infotxt" style="margin-left: 2px; text-decoration: underline; cursor: pointer; display: block;">(Удалить все)</a>', '</div>', '<a class="h" style="margin-top: 5px;">Добавить язык:</a>', '<select class="select-language" style="margin-left: 2px; margin-top: 5px;"></select>', '<a class="infotxt" style="margin-left: 2px;">Выбирете язык из списка. Он автоматически добавится в существующий список.</a>', '<a id="ExtensionError" class="infotxt" style="display: none;; margin-left: 2px; color: red; font-weight: bold;"></a>', '<div id="g-signin2" class="flx flx-row"><div id="LoginGoogle" class="g-signin2 flx flx-center"><a style="color: black;">Войти</a></div><div id="Extension-Starts" class="flx flx-center"><a style="color: white;">Старт</a></div></div>', '</div>', '<div class="loading flx flx-column" style="display: none;">', '<a class="h">Обрабатываем...</a>', '<a class="infotxt" id="Extension-Status"></a>', '</div>', '</div>' ].join(''); $("body").prepend(popup); setInterval(awaitAddButton, 2500); $("#Extension-Starts").on("click", function(evt) { var UrlQueryes = getUrlVars(); var Name = MyName; if (Name === undefined || Name === null || Name == '') { abort("Выполните вход нажав на кнопку Google."); return; } $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id='+UrlQueryes.videoId+'&key='+APIKey, type: "GET", headers: { "Accept": "application/json" }, processData: false, success: function(response) { startTranslate(response, UrlQueryes.videoId, response.items[0].snippet.categoryId, response.items[0].snippet.tags, response.items[0].snippet.thumbnails); } }); }); $("#DeleteAllLang").on("click", function() { loadedLanguages = []; drawLanguages(); }); $("#popup-meta-translator-extension").click( function(event) { if ($("#popup-meta-translator-extension").css("display") !== "none") { if( $(event.target).closest("#popup-meta-translator-extension .content").length ) return; $("#popup-meta-translator-extension").hide(); event.stopPropagation(); } }); languagesToSelect(); drawLanguages(); $(".select-language").change(function(evt) { var code = $(".select-language").val(); if(loadedLanguages.indexOf(code) == -1) { loadedLanguages.push(code); drawLanguages(); } }); handleClientLoad(); } function timerSetter() { if ($(BtnAppend) != undefined && $(BtnAppend) != '') { clearInterval(adder); setTimeout(createElement, 2000); } } adder = setInterval(timerSetter, 1000); function drawLanguages() { var searched; localStorage.setItem('YoutubeTranslator', JSON.stringify(loadedLanguages)); $(".selected-languages").html(''); for (var i = 0; i < loadedLanguages.length; i++) { let code = loadedLanguages[i]; if (Languages.translation[code] !== undefined && Languages.translation[code].name !== undefined) { $(".selected-languages").append('<a code="'+code+'">'+Languages.translation[code].name+'</a>'); } else { delete loadedLanguages[code]; localStorage.setItem('YoutubeTranslator', JSON.stringify(loadedLanguages)); } } if (loadedLanguages.length > 0) { $(".selected-languages-h").show(); } else { $(".selected-languages-h").hide(); } $(".selected-languages a").on("click", function(evt) { let code = $(evt.currentTarget).attr("code"); loadedLanguages.splice(loadedLanguages.indexOf(code), 1); drawLanguages(); evt.stopPropagation(); }); } } async function startTranslate(response, query, category, tags, thumbnails) { $("#ExtensionError").hide(); if (APIKey === undefined || APIKey === '') { abort('У вас не указан API ключ! Создайте его по данной ссылке: https://console.cloud.google.com/projectselector2/home/dashboard'); return; } if (AppCode == '' || XRapidMicrosoftKey == '') { abort('Не указан AppCode (OAuth Client ID) или XRapidMicrosoftKey. Укажите их в скрипте. Для этого нажмите на Tampermonkey > Youtube Translate > Редактировать.'); return; } if (loadedLanguages === undefined || loadedLanguages === null || loadedLanguages.length == 0 || loadedLanguages == '') { abort('Вы не указали ни 1 языка.'); return; } var snippet = response.items[0].snippet; async function translate_microsoft(text, to) { var json; var dates = [{"Text":text}]; const settings = { "async": true, "crossDomain": true, "url": "https://microsoft-translator-text.p.rapidapi.com/translate?to="+to+"&api-version=3.0&profanityAction=NoAction&textType=plain", "method": "POST", "headers": { "content-type": "application/json", "x-rapidapi-key": XRapidMicrosoftKey, "x-rapidapi-host": "microsoft-translator-text.p.rapidapi.com" }, "processData": false, "data": JSON.stringify(dates), "dataType":"JSON" }; async function sendReq() { $.ajax(settings).done(function (response) { json = response[0].translations[0].text; }); } await sendReq(); return json; } Translated = { "data": [] }; Translated.title = snippet.title; Translated.description = snippet.description; Translated.id = query; Translated.category = category; Translated.tags = tags; Translated.thumbnails = thumbnails; // console.log(Translated); if (TranslateBy === 'Microsoft') { var i = 0; $("#popup-meta-translator-extension >").hide(); $(".loading").show(); loadDraw(); async function translateElements() { var temp = {}; temp.code = loadedLanguages[i]; var AjaxSettings = { "url": "https://microsoft-translator-text.p.rapidapi.com/translate?to="+temp.code+"&api-version=3.0&profanityAction=NoAction&textType=plain", "async": true, "crossDomain": true, "method": "POST", "headers": { "content-type": "application/json", "x-rapidapi-key": XRapidMicrosoftKey, "x-rapidapi-host": "microsoft-translator-text.p.rapidapi.com" }, "processData": false, "dataType":"JSON" }; titleTranslate(); function titleTranslate() { AjaxSettings.data = JSON.stringify([{"Text":snippet.title}]); $.ajax(AjaxSettings).done(function (response) { temp.title = response[0].translations[0].text; descriptionTranslate(); }); } function descriptionTranslate() { AjaxSettings.data = JSON.stringify([{"Text":snippet.description}]); $.ajax(AjaxSettings).done(function (response) { temp.description = response[0].translations[0].text; Translated.lang = response[0].detectedLanguage.language; repeatContinue(temp) }); } function repeatContinue(value) { Translated.data[i] = value; loadDraw(); i = i+1; if (i < loadedLanguages.length) { translateElements(); } else { addDefaultLanguage(); } } } function loadDraw() { $("#Extension-Status").html('Переводим текст '+(i+1)+'/'+loadedLanguages.length+' ('+loadedLanguages[i]+')'); } translateElements(); } } function translateFinish(text) { $("#Extension-Status").html(text); setTimeout(function() { $("#popup-meta-translator-extension .content").show(); $("#popup-meta-translator-extension .loading").hide(); }, 5000); } function addDefaultLanguage() { $("#Extension-Status").html('Добавляем язык по умолчанию...'); // console.log(Translated); var req = { "id": Translated.id, "snippet": { "title": Translated.title, "description": Translated.description, "categoryId": Translated.category, "tags": Translated.tags, // "thumbnails": Translated.thumbnails, "defaultLanguage": Translated.lang } } // console.log(req); req = JSON.stringify(req); $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet&alt=json&key='+APIKey, type: "PUT", headers: { "Authorization": "Bearer "+AccessToken, "Accept": "application/json", "Content-Type": "application/json" }, processData: false, data: req, success: function(response) { addLanguagesToVideo(); } }); addLanguagesToVideo(); } function addLanguagesToVideo() { var req = { "id": Translated.id, "localizations": {} } createJSON(); async function createJSON() { $("#Extension-Status").html('Создаём список языков...'); for (var i = 0; i < Translated.data.length; i++) { let ins = { "title": Translated.data[i].title, "description": Translated.data[i].description } req.localizations[Translated.data[i].code] = ins; } addLanguageVID(); } async function addLanguageVID() { $("#Extension-Status").html('Добавляем языки к видео...'); req = JSON.stringify(req); $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=localizations&alt=json&key='+APIKey, type: "PUT", headers: { "Authorization": "Bearer "+AccessToken, "Accept": "application/json", "Content-Type": "application/json" }, processData: false, data: req, success: function(response) { translateFinish('Успешно!'); }, error: function(response) { translateFinish('Произошла ошибка. Похоже, какой-то из языков не поддерживается ютубом.'); } }); } } function getUrlVars() { var data = {}; var part = window.location.pathname; part = part.split('/'); data.type = part[1]; data.videoId = part[2]; data.category = part[3]; return data; } function abort(text) { $("#ExtensionError").html(text).show(); } var GoogleAuth; var SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl'; async function handleClientLoad() { gapi.load('client:auth2', initClient); } function initClient() { gapi.client.init({ 'apiKey': APIKey, 'clientId': AppCode, 'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest'], 'scope': SCOPE }).then(function () { GoogleAuth = gapi.auth2.getAuthInstance(); GoogleAuth.isSignedIn.listen(updateSigninStatus); var user = GoogleAuth.currentUser.get(); if (user.xc !== null && user.xc.access_token !== null) { AccessToken = user.xc.access_token; MyName = user.wt.Ad; } setSigninStatus(); $('#LoginGoogle').click(function() { handleAuthClick(); }); }); } function handleAuthClick() { if (GoogleAuth.isSignedIn.get() === true) { GoogleAuth.signOut(); GoogleAuth.disconnect(); setTimeout(function() { awaitSignIn(); }, 5000); } else { GoogleAuth.signIn(); } updateSigninStatus(); } function awaitSignIn() { if (GoogleAuth.isSignedIn.get() === true) { initClient(); } else { setTimeout(awaitSignIn, 1000); } } async function setSigninStatus() { var user = GoogleAuth.currentUser.get(); var isAuthorized = user.hasGrantedScopes(SCOPE); if (isAuthorized) { $("#LoginGoogle a").html('Выход'); } else { $("#LoginGoogle a").html('Войти'); } } async function updateSigninStatus() { setSigninStatus();} document.addEventListener('keydown', function(event) { if (event.code == 'KeyZ') { let id = prompt("Укажите ID видео:"); $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id='+id+'&key='+APIKey, type: "GET", headers: { // "Authorization": "Bearer "+$("#popup-meta-translator-extension").attr("access_token"), "Accept": "application/json" }, processData: false, success: function(response) { console.log(response.items[0].snippet); } }); } else if (event.code == 'KeyX') { let id = prompt("Укажите ID видео:"); $("#popup-meta-translator-extension >").hide(); $(".loading").show(); $("#Extension-Status").html('Получаем данные для запроса...'); $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id='+id+'&key='+APIKey, type: "GET", headers: { "Accept": "application/json" }, processData: false, success: function(response) { nextStepTest(response.items[0]); } }); function nextStepTest(snippet) { $("#Extension-Status").html('Тестируем добавление данных...'); var req = { "id": snippet.id, "snippet": { "title": snippet.snippet.title, "description": snippet.snippet.description, "categoryId": snippet.snippet.categoryId, "tags": snippet.snippet.tags, // "thumbnails": Translated.thumbnails, "defaultLanguage": "en" } } req = JSON.stringify(req); $.ajax({ url: 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet&alt=json&key='+APIKey, type: "PUT", headers: { "Authorization": "Bearer "+AccessToken, "Accept": "application/json", "Content-Type": "application/json" }, processData: false, data: req, success: function(response) { translateFinish('Success!'); }, error: function(xhr, status, error) { translateFinish(xhr.responseText); } }); } } }); })();