mirror of
https://github.com/ilyhalight/voice-over-translation.git
synced 2026-05-01 21:10:25 +00:00
first commit
This commit is contained in:
commit
f67afc19a8
5 changed files with 208 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -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.
|
||||
11
README.md
Normal file
11
README.md
Normal file
|
|
@ -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)**
|
||||
|
||||

|
||||
BIN
example.png
Normal file
BIN
example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
18
styles.css
Normal file
18
styles.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
158
vot.user.js
Normal file
158
vot.user.js
Normal file
|
|
@ -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>");
|
||||
|
||||
$(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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue