commit f67afc19a8fe2c5ee462a27b2414a68a9052dfe1 Author: sodapng Date: Tue Dec 14 21:21:49 2021 +0400 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..16553a10 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 sodapng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..f830e6ec --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Voice-over translation + +Voice-over translation not only in Yandex.Browser.\ +Much obliged, **[Yandex.Translate](https://translate.yandex.ru/)**. Thank you. + +## Как использовать: + +1. Установить расширение **[violetmonkey](https://violentmonkey.github.io/get-it/)** +2. **[«Установить Скрипт»](https://raw.githubusercontent.com/sodapng/voice-over-translation/master/vot.user.js)** + +![example btn](https://github.com/sodapng/voice-over-translation/blob/master/example.png "btn") diff --git a/example.png b/example.png new file mode 100644 index 00000000..16334752 Binary files /dev/null and b/example.png differ diff --git a/styles.css b/styles.css new file mode 100644 index 00000000..16886f2a --- /dev/null +++ b/styles.css @@ -0,0 +1,18 @@ +.translation-btn { + box-sizing: border-box; + width: 5rem; + padding: 1.5rem; + border-radius: 0.5rem; + background: no-repeat center black + url(https://icongr.am/entypo/language.svg?size=25&color=ffffff); + position: absolute; + top: 3rem; + left: 50%; + box-shadow: 0px 0px 10px 2px rgba(34, 60, 80, 0.5); + opacity: 0; + cursor: pointer; +} + +.translation-btn:hover { + opacity: 1; +} diff --git a/vot.user.js b/vot.user.js new file mode 100644 index 00000000..49c1f3bc --- /dev/null +++ b/vot.user.js @@ -0,0 +1,158 @@ +// ==UserScript== +// @name voice-over-translation +// @match *://*.youtube.com/* +// @version 1.0 +// @require https://code.jquery.com/jquery-3.6.0.min.js +// @resource styles https://raw.githubusercontent.com/sodapng/voice-over-translation/master/styles.css +// @grant GM_getResourceText +// @grant GM_addStyle +// @grant GM_xmlhttpRequest +// @updateURL https://raw.githubusercontent.com/sodapng/voice-over-translation/master/vot.user.js +// @downloadURL https://raw.githubusercontent.com/sodapng/voice-over-translation/master/vot.user.js +// @supportURL https://github.com/sodapng/voice-over-translation/issues +// @homepageURL https://github.com/sodapng/voice-over-translation +// ==/UserScript== + +const styles = GM_getResourceText("styles"); +GM_addStyle(styles); + +const fragment = new DocumentFragment(); +const span = $(""); + +$(span).addClass("translation-btn"); + +fragment.appendChild(span[0]); +const audio = new Audio(); + +const getVeideoId = () => { + const url = new URL(window.location.href); + + if (url.pathname.includes("watch")) { + return url.searchParams.get("v"); + } + + if (url.pathname.includes("embed")) { + return url.pathname.substr(7, 11); + } + + return false; +}; + +const getUrlAudio = (videoId) => { + return new Promise((resolve, reject) => { + GM_xmlhttpRequest({ + url: "https://api.browser.yandex.ru/video-translation/translate", + method: "POST", + headers: { + "Content-Type": "application/x-protobuf", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaBrowser/21.11.2.773 Yowser/2.5 Safari/537.36", + }, + data: `\x1A\x1Chttps://youtu.be/${videoId}\x28\x01`, + onload: (res) => { + if (res.total === 40 || res.total === 42) { + return reject("VIDEO-TRANSLATION: can not translate"); + } + + if (res.status === 200) { + return resolve(res.response); + } + + return reject("VIDEO-TRANSLATION: bad request"); + }, + onerror: (err) => { + return reject("VIDEO-TRANSLATION: connection error"); + }, + }); + }); +}; + +const deleteAudioSrc = () => { + audio.src = ""; + audio.removeAttribute("src"); +}; + +$("body").on("yt-page-data-updated", function () { + var video = $("video")[0]; + $(".html5-video-container").append(fragment); + + const lipSync = (mode = false) => { + audio.currentTime = video.currentTime; + // audio.volume = video.volume + + if (!mode) { + return; + } + + if (mode === "play") { + audio.play(); + } + + if (mode === "pause") { + audio.pause(); + } + }; + + $(span).click(function (event) { + event.stopPropagation(); + + if (audio.src) { + deleteAudioSrc(); + event.stopImmediatePropagation(); + } + }); + + $(span).click(async function (event) { + try { + event.stopPropagation(); + + const VIDEO_ID = getVeideoId(); + + if (!VIDEO_ID) { + throw "VIDEO-TRANSLATION: not found video id"; + } + + const rawResponse = await getUrlAudio(VIDEO_ID); + + const URL_AUDIO = rawResponse.match(/https.*[a-z0-9]{64}|https.*mp3/); + + if (!URL_AUDIO) { + throw "VIDEO-TRANSLATION: raw string error"; + } + + audio.src = URL_AUDIO[0]; + + $("body").one("yt-page-data-updated", function () { + audio.pause(); + $("video").off(".translate"); + deleteAudioSrc(); + }); + + if (!video.paused) { + lipSync("play"); + } + + $("video").on("playing.translate", function () { + lipSync(); + }); + + $("video").on("play.translate canplaythrough.translate", function () { + lipSync(); + + if (!video.paused) { + lipSync("play"); + } + }); + + $("video").on("pause.translate waiting.translate", function () { + lipSync("pause"); + }); + } catch (err) { + if (!err) { + console.error("something went wrong"); + } + + console.error(err); + } + }); +});