2020-06-18 08:12:56 +00:00
|
|
|
const translation_token_generator = require('google-translate-token');
|
|
|
|
const bodyParser = require('body-parser');
|
|
|
|
const express = require('express');
|
|
|
|
const request = require('request');
|
|
|
|
const cors = require('cors');
|
|
|
|
const fs = require('fs');
|
|
|
|
|
2020-06-20 09:50:52 +00:00
|
|
|
|
2020-06-18 08:12:56 +00:00
|
|
|
var server = express();
|
|
|
|
server.use(cors());
|
|
|
|
server.use(express.urlencoded({extended: true}));
|
|
|
|
server.use(bodyParser.json({extended: true}));
|
|
|
|
|
|
|
|
const index_page = `
|
|
|
|
<!DOCUMENT html>
|
|
|
|
<html lang="br">
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<title>Tradutor simultâneo</title>
|
|
|
|
<link rel="stylesheet" type="text/css" href="/main.css" />
|
|
|
|
<script src="/main.js" defer></script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div>
|
|
|
|
<div id="cfgformp">
|
|
|
|
<h3 class="nospacing">Configurações</h3>
|
|
|
|
<form id="cfgform" onsubmit="return false;">
|
|
|
|
<div class="formgroup">
|
|
|
|
<h5 class="nospacing">Idioma da fala</h5>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="ilpt" name="il" value="1" checked>
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="ilpt">PT</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="ilen" name="il" value="2">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="ilen">EN</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="iles" name="il" value="3">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="iles">ES</label><br>
|
|
|
|
</div>
|
|
|
|
<div class="formgroup">
|
|
|
|
<h5 class="nospacing">Idioma da 1ª tradução</h5>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="1lpt" name="1l" value="1">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="1lpt">PT</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="1len" name="1l" value="2">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="1len">EN</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="1les" name="1l" value="3">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="1les">ES</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="1l_" name="1l" value="0" checked>
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="1l_">Ignorar</label><br>
|
|
|
|
</div>
|
|
|
|
<div class="formgroup">
|
|
|
|
<h5 class="nospacing">Idioma da 2ª tradução</h5>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="2lpt" name="2l" value="1">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="2lpt">PT</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="2len" name="2l" value="2">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="2len">EN</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="2les" name="2l" value="3">
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="2les">ES</label><br>
|
2020-06-20 19:39:22 +00:00
|
|
|
<input type="radio" id="2l_" name="2l" value="0" checked>
|
2020-06-18 08:12:56 +00:00
|
|
|
<label for="2l_">Ignorar</label><br>
|
|
|
|
</div>
|
|
|
|
<div class="formgroup">
|
2020-06-21 00:17:05 +00:00
|
|
|
<h5 class="nospacing">Visual</h5>
|
|
|
|
<label for="ff">Família de fontes:</label>
|
2020-06-20 20:42:50 +00:00
|
|
|
<input type="text" id="ff" name="fontfamily" value="sans-serif">
|
|
|
|
<br>
|
2020-06-21 00:17:05 +00:00
|
|
|
<label for="fs">Tamanho:</label>
|
|
|
|
<input type="number" step="0.1" id="fs" name="fontsize" value="3"> em
|
|
|
|
<br>
|
|
|
|
<label for="bh">Altura:</label>
|
|
|
|
<input type="number" step="0.1" id="bh" name="boxheight" value="6.6"> ex
|
|
|
|
<br>
|
|
|
|
<label for="clr">Cor:</label>
|
|
|
|
<input type="text" step="0.1" id="clr" name="color" value="#FFFFFF">
|
|
|
|
<br>
|
|
|
|
</div>
|
|
|
|
<div class="formgroup">
|
|
|
|
<h5 class="nospacing">Controle</h5>
|
2020-06-20 20:42:50 +00:00
|
|
|
<input type="button" id="apl" value="Aplicar" onclick="return aplicar();">
|
2020-06-18 08:12:56 +00:00
|
|
|
</div>
|
2020-06-20 09:50:52 +00:00
|
|
|
<div class="formgroup">
|
|
|
|
<h5 class="nospacing">Estatísticas</h5>
|
|
|
|
<div><span>Traduções solicitadas: </span><span id="trad_count"></span></div>
|
|
|
|
<div><span>Traduções recebidas: </span><span id="trans_count"></span></div>
|
|
|
|
</div>
|
2020-06-18 08:12:56 +00:00
|
|
|
</form>
|
|
|
|
<hr>
|
|
|
|
</div>
|
|
|
|
<div class="loader">
|
|
|
|
<h2>Configure antes de usar</h2>
|
|
|
|
<hr>
|
|
|
|
</div>
|
|
|
|
<dl id="transcriptions">
|
|
|
|
<dt id="i1dt">fala</dt>
|
|
|
|
<dd id="i1dd"><div class="listened"></div></dd>
|
|
|
|
<dt id="o1dt">trad1</dt>
|
|
|
|
<dd id="o1dd"><div class="translated1"></div></dd>
|
|
|
|
<dt id="o2dt">trad2</dt>
|
|
|
|
<dd id="o2dd"><div class="translated2"></div></dd>
|
|
|
|
</dl>
|
|
|
|
<hr>
|
2020-06-21 00:17:05 +00:00
|
|
|
<div><span class="widthTestArea"></span></div>
|
2020-06-18 08:12:56 +00:00
|
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
`.trim();
|
|
|
|
|
|
|
|
const main_style = `
|
|
|
|
body {
|
|
|
|
font-family: sans-serif;
|
|
|
|
background-color: black;
|
|
|
|
color: white;
|
|
|
|
}
|
|
|
|
dl#transcriptions {
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
}
|
|
|
|
dl#transcriptions>dt {
|
|
|
|
display: block;
|
|
|
|
max-width: 4rem;
|
|
|
|
flex: 0 0 4rem;
|
|
|
|
border-right: 1px solid white;
|
|
|
|
border-bottom: 1px solid white;
|
|
|
|
}
|
|
|
|
dl#transcriptions>dd {
|
|
|
|
margin-inline-start: 4em;
|
|
|
|
max-width: calc( 100% - 9rem );
|
|
|
|
flex: 0 0 calc( 100% - 9rem );
|
|
|
|
overflow-y: hidden;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column-reverse;
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
dl#transcriptions>dt, dl#transcriptions>dd {
|
|
|
|
align-items: baseline;
|
|
|
|
margin: 1rem;
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
}
|
|
|
|
form {
|
|
|
|
display: flex;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
}
|
|
|
|
.nospacing {
|
|
|
|
margin: 0px;
|
|
|
|
padding: 0px;
|
|
|
|
}
|
|
|
|
.formgroup {
|
|
|
|
padding-left: 1rem;
|
2020-06-20 20:42:50 +00:00
|
|
|
}
|
|
|
|
.formgroup:not(:last-of-type) {
|
2020-06-18 08:12:56 +00:00
|
|
|
border-right: 1px solid white;
|
2020-06-20 20:42:50 +00:00
|
|
|
padding-right: 1rem;
|
2020-06-18 08:12:56 +00:00
|
|
|
}
|
2020-06-21 00:17:05 +00:00
|
|
|
form input[type=button], form input[type=text], form input[type=number] {
|
2020-06-20 09:50:52 +00:00
|
|
|
background: black;
|
|
|
|
color: white;
|
|
|
|
border: white 0.08rem solid;
|
|
|
|
border-radius: 0.2rem;
|
|
|
|
padding: 0.2rem;
|
|
|
|
padding-left: 0.4rem;
|
|
|
|
padding-right: 0.4rem;
|
|
|
|
}
|
2020-06-20 20:42:50 +00:00
|
|
|
form input[type=text] {
|
2020-06-21 00:17:05 +00:00
|
|
|
width: 6rem;
|
|
|
|
}
|
|
|
|
form input[type=number] {
|
|
|
|
width: 3rem;
|
2020-06-20 20:42:50 +00:00
|
|
|
}
|
2020-06-18 08:12:56 +00:00
|
|
|
`.trim();
|
|
|
|
|
|
|
|
const main_script = `
|
|
|
|
window.SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
|
|
|
|
|
2020-06-20 19:39:22 +00:00
|
|
|
LANGS_1 = [null, 'pt', 'en', 'es'];
|
|
|
|
LANGS_2 = [null, 'pt-BR', 'en-US', 'es-AR'];
|
2020-06-18 08:12:56 +00:00
|
|
|
SPOKEN_LANG_POS = 0;
|
|
|
|
SELECTED_LANG1_POS = 1;
|
|
|
|
SELECTED_LANG2_POS = 2;
|
|
|
|
|
|
|
|
recognizing = false;
|
|
|
|
recognition = new Object();
|
|
|
|
recognition.continuous = false;
|
|
|
|
recognition.interimResults = true;
|
|
|
|
recognition.lang = LANGS_2[SPOKEN_LANG_POS];
|
|
|
|
recog_id = generate_random_int_id();
|
|
|
|
|
|
|
|
from_lang = LANGS_1[SPOKEN_LANG_POS];
|
|
|
|
to_lang1 = LANGS_1[SELECTED_LANG1_POS];
|
|
|
|
to_lang2 = LANGS_1[SELECTED_LANG2_POS];
|
|
|
|
|
|
|
|
loader = document.querySelector('.loader');
|
|
|
|
listened = document.querySelector('.listened');
|
|
|
|
translated1 = document.querySelector('.translated1');
|
|
|
|
translated2 = document.querySelector('.translated2');
|
2020-06-21 00:17:05 +00:00
|
|
|
widthTestArea = document.querySelector('.widthTestArea');
|
2020-06-18 08:12:56 +00:00
|
|
|
cfgform = document.querySelector('#cfgform');
|
|
|
|
cfgformp = document.querySelector('#cfgformp');
|
2020-06-20 09:50:52 +00:00
|
|
|
trad_count = document.querySelector('#trad_count');
|
|
|
|
trans_count = document.querySelector('#trans_count');
|
2020-06-18 08:12:56 +00:00
|
|
|
bg = document.querySelector('html');
|
|
|
|
|
|
|
|
recognition.onstart = function() {
|
|
|
|
recognizing = true;
|
|
|
|
console.log('onstart');
|
|
|
|
}
|
|
|
|
recognition.onerror = function(event) {
|
|
|
|
console.log('onerror', event);
|
|
|
|
aplicar();
|
|
|
|
}
|
|
|
|
recognition.onend = function() {
|
|
|
|
recognizing = false;
|
|
|
|
console.log('onend');
|
|
|
|
aplicar();
|
|
|
|
}
|
|
|
|
recognition.onresult = function(event) {
|
|
|
|
var copied = new Object();
|
|
|
|
copied.lang = from_lang;
|
|
|
|
copied.resultIndex = event.resultIndex;
|
|
|
|
copied.identity = recog_id;
|
|
|
|
copied.results = new Array();
|
|
|
|
for(var i = 0; i < event.results.length; i++) {
|
|
|
|
speech_recognition_result = new Object();
|
|
|
|
speech_recognition_result.isFinal = event.results[i].isFinal;
|
|
|
|
speech_recognition_result.alternatives = new Array();
|
|
|
|
for(var j = 0; j < event.results[i].length; j++) {
|
|
|
|
speech_recognition_alternative = new Object();
|
|
|
|
speech_recognition_alternative.transcript = event.results[i][j].transcript;
|
|
|
|
speech_recognition_alternative.confidence = event.results[i][j].confidence;
|
|
|
|
speech_recognition_result.alternatives.push(speech_recognition_alternative);
|
|
|
|
}
|
|
|
|
copied.results.push(speech_recognition_result);
|
|
|
|
}
|
|
|
|
doXhr(
|
|
|
|
'POST',
|
|
|
|
'/set/speech',
|
|
|
|
()=>{},
|
|
|
|
()=>{},
|
|
|
|
JSON.stringify(copied),
|
|
|
|
"application/json"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function aplicar_conf() {
|
|
|
|
newvalues = Object.fromEntries(
|
|
|
|
Array.from(
|
|
|
|
cfgform.querySelectorAll(':checked')
|
|
|
|
).map(x=>[x.name, parseInt(x.value)])
|
2020-06-20 20:42:50 +00:00
|
|
|
);
|
|
|
|
fontfamily = document.getElementById('ff').value;
|
2020-06-21 00:17:05 +00:00
|
|
|
fontsize = '' + document.getElementById('fs').value + 'em';
|
|
|
|
boxheight = '' + document.getElementById('bh').value + 'ex';
|
|
|
|
textcolor = '' + document.getElementById('clr').value;
|
2020-06-20 19:39:22 +00:00
|
|
|
SPOKEN_LANG_POS = newvalues['il'] || 0;
|
|
|
|
SELECTED_LANG1_POS = newvalues['1l'] || 0;
|
|
|
|
SELECTED_LANG2_POS = newvalues['2l'] || 0;
|
2020-06-18 08:12:56 +00:00
|
|
|
from_lang = LANGS_1[SPOKEN_LANG_POS] || null;
|
|
|
|
to_lang1 = LANGS_1[SELECTED_LANG1_POS] || null;
|
|
|
|
to_lang2 = LANGS_1[SELECTED_LANG2_POS] || null;
|
2020-06-20 20:42:50 +00:00
|
|
|
listened.style.fontFamily = fontfamily;
|
|
|
|
translated1.style.fontFamily = fontfamily;
|
|
|
|
translated2.style.fontFamily = fontfamily;
|
2020-06-21 00:17:05 +00:00
|
|
|
widthTestArea.style.fontFamily = fontfamily;
|
|
|
|
listened.style.fontSize = fontsize;
|
|
|
|
translated1.style.fontSize = fontsize;
|
|
|
|
translated2.style.fontSize = fontsize;
|
|
|
|
widthTestArea.style.fontSize = fontsize;
|
|
|
|
listened.style.height = boxheight;
|
|
|
|
translated1.style.height = boxheight;
|
|
|
|
translated2.style.height = boxheight;
|
|
|
|
listened.style.color = textcolor;
|
|
|
|
translated1.style.color = textcolor;
|
|
|
|
translated2.style.color = textcolor;
|
|
|
|
listened.style.display = 'flex';
|
|
|
|
translated1.style.display = 'flex';
|
|
|
|
translated2.style.display = 'flex';
|
|
|
|
listened.style.flexDirection = 'column-reverse';
|
|
|
|
translated1.style.flexDirection = 'column-reverse';
|
|
|
|
translated2.style.flexDirection = 'column-reverse';
|
2020-06-18 08:12:56 +00:00
|
|
|
if (from_lang === null) return;
|
|
|
|
recognition_old = recognition;
|
|
|
|
recognition = new SpeechRecognition();
|
|
|
|
recognition.lang = LANGS_2[SPOKEN_LANG_POS] || null;
|
|
|
|
recognition.onstart = recognition_old.onstart;
|
|
|
|
recognition.onerror = recognition_old.onerror;
|
|
|
|
recognition.onend = recognition_old.onend;
|
|
|
|
recognition.onresult = recognition_old.onresult;
|
|
|
|
recognition.continuous = recognition_old.continuous;
|
|
|
|
recognition.interimResults = recognition_old.interimResults;
|
|
|
|
recognition_old.onend = null;
|
|
|
|
recognition_old.onerror = null;
|
|
|
|
recog_id = generate_random_int_id();
|
|
|
|
}
|
|
|
|
function aplicar(){
|
2020-06-21 00:17:05 +00:00
|
|
|
loader.style.display = 'none';
|
2020-06-18 08:12:56 +00:00
|
|
|
aplicar_conf();
|
|
|
|
recognition.start();
|
|
|
|
doXhr(
|
|
|
|
'POST',
|
|
|
|
'/set/config',
|
|
|
|
()=>{},
|
|
|
|
()=>{},
|
|
|
|
JSON.stringify({
|
|
|
|
'from_lang': from_lang,
|
|
|
|
'to_lang1': to_lang1,
|
|
|
|
'to_lang2': to_lang2,
|
|
|
|
}),
|
|
|
|
"application/json");
|
|
|
|
}
|
|
|
|
|
|
|
|
function doXhr(method, URL, onsuccess, onfailure, body, bodyMime) {
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.open(method, URL);
|
|
|
|
xhr.onload = function() {
|
|
|
|
if (xhr.status != 200)
|
|
|
|
onfailure(xhr);
|
|
|
|
else
|
|
|
|
onsuccess(xhr);
|
|
|
|
};
|
|
|
|
if (bodyMime) xhr.setRequestHeader("Content-Type", bodyMime);
|
|
|
|
xhr.onerror = () => onfailure(xhr);
|
|
|
|
xhr.send(body);
|
|
|
|
return xhr;
|
|
|
|
}
|
|
|
|
|
|
|
|
function generate_random_int_id() {
|
|
|
|
return Math.trunc(Math.random()*Math.pow(2, 22));
|
|
|
|
}
|
|
|
|
|
|
|
|
function check_translations() {
|
|
|
|
setTimeout(check_translations, 100);
|
|
|
|
doXhr(
|
|
|
|
'GET',
|
|
|
|
'/get/speech',
|
|
|
|
(xhr)=>{
|
|
|
|
var translations = JSON.parse(xhr.responseText);
|
2020-06-20 09:50:52 +00:00
|
|
|
listened.innerText = translations.boxes[0];
|
|
|
|
translated1.innerText = translations.boxes[1];
|
|
|
|
translated2.innerText = translations.boxes[2];
|
|
|
|
trad_count.innerText = translations.translation_count;
|
|
|
|
trans_count.innerText = translations.translated_count;
|
2020-06-21 00:17:05 +00:00
|
|
|
measure_words(listened);
|
|
|
|
measure_words(translated1);
|
|
|
|
measure_words(translated2);
|
2020-06-18 08:12:56 +00:00
|
|
|
},
|
|
|
|
()=>{}
|
|
|
|
);
|
|
|
|
}
|
2020-06-21 00:17:05 +00:00
|
|
|
|
|
|
|
function measure_words(box){
|
|
|
|
wordlist = box.innerText.split(' ').filter(x=>x.length);
|
|
|
|
wordlist = [...(new Set(wordlist.join(' ').split(' ').filter(x=>x.length)))];
|
|
|
|
wordoffsets = {};
|
|
|
|
widthTestArea.innerText = 'a a';
|
|
|
|
var a_a = widthTestArea.offsetWidth;
|
|
|
|
widthTestArea.innerText = 'aa';
|
|
|
|
var aa = widthTestArea.offsetWidth;
|
|
|
|
var _ = a_a - aa;
|
|
|
|
wordoffsets[' '] = _;
|
|
|
|
for (let word of wordlist) {
|
|
|
|
widthTestArea.innerText = word;
|
|
|
|
wordoffsets[word] = widthTestArea.offsetWidth;
|
|
|
|
}
|
|
|
|
widthTestArea.innerText = '';
|
|
|
|
measurement_summary = {};
|
|
|
|
measurement_summary.wordWidths = wordoffsets;
|
|
|
|
measurement_summary.boxWidth = box.offsetWidth;
|
|
|
|
doXhr(
|
|
|
|
'POST',
|
|
|
|
'/set/measurements',
|
|
|
|
()=>{},
|
|
|
|
()=>{},
|
|
|
|
JSON.stringify(measurement_summary),
|
|
|
|
"application/json");
|
|
|
|
}
|
|
|
|
|
2020-06-18 08:12:56 +00:00
|
|
|
setTimeout(check_translations, 100);
|
2020-06-21 00:17:05 +00:00
|
|
|
aplicar_conf();
|
2020-06-18 08:12:56 +00:00
|
|
|
`.trim();
|
|
|
|
|
|
|
|
var past_input_strings = [];
|
|
|
|
var latest_input_id = null;
|
|
|
|
var latest_input_string = '';
|
2020-06-21 00:17:05 +00:00
|
|
|
var assembled_input_string = '';
|
2020-06-18 08:12:56 +00:00
|
|
|
var out_strings = ['', '', ''];
|
|
|
|
var lang_array = ['pt', null, null];
|
|
|
|
var last_translations = {};
|
2020-06-21 00:17:05 +00:00
|
|
|
const silence_timeout = 2500;
|
|
|
|
var silence_timeout_id = null;
|
|
|
|
const translation_timeout = 1500;
|
2020-06-18 08:12:56 +00:00
|
|
|
var translator_locked = false;
|
2020-06-20 20:42:50 +00:00
|
|
|
var translation_bounced_due_to_lock = false;
|
2020-06-20 09:50:52 +00:00
|
|
|
const box_names = ['fala', 'trad1', 'trad2'];
|
|
|
|
var translations_ordered = 0;
|
|
|
|
var translations_received = 0;
|
2020-06-21 00:17:05 +00:00
|
|
|
const translation_word_count = 40;
|
|
|
|
const max_past_input_strings = 5;
|
|
|
|
var word_measurements = {};
|
|
|
|
var current_box_width = 0;
|
2020-06-18 08:12:56 +00:00
|
|
|
|
|
|
|
function update_file(location, new_content) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
var errCallback = (err) => {
|
|
|
|
if (err) reject(err);
|
|
|
|
return !!err;
|
|
|
|
};
|
|
|
|
var writeFileCallback = (err) => {
|
|
|
|
if(!errCallback(err)) resolve(true);
|
|
|
|
}
|
|
|
|
fs.exists(location, exists => {
|
|
|
|
if(!exists)
|
|
|
|
fs.writeFile(
|
|
|
|
location,
|
|
|
|
new_content,
|
|
|
|
undefined,
|
|
|
|
writeFileCallback);
|
|
|
|
else {
|
|
|
|
fs.readFile(location, (err, old_content) => {
|
|
|
|
if (!errCallback(err)) {
|
|
|
|
if (old_content==new_content)
|
|
|
|
resolve(true);
|
|
|
|
else
|
|
|
|
fs.writeFile(
|
|
|
|
location,
|
|
|
|
new_content,
|
|
|
|
undefined,
|
|
|
|
writeFileCallback);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function do_translate(text, lang_from, lang_to) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
if(!text) return resolve(text);
|
|
|
|
if(!lang_from) return resolve(text);
|
|
|
|
if(!lang_to) return resolve(text);
|
|
|
|
translation_token_generator.get(text).then((token) => {
|
|
|
|
return request.post(
|
|
|
|
'https://translate.google.com/translate_a/single?'+
|
|
|
|
'client=webapp&'+
|
|
|
|
`sl=${lang_from}&`+
|
|
|
|
`tl=${lang_to}&`+
|
|
|
|
`hl=${lang_from}&`+
|
|
|
|
'dt=at&'+
|
|
|
|
'dt=bd&'+
|
|
|
|
'dt=ex&'+
|
|
|
|
'dt=ld&'+
|
|
|
|
'dt=md&'+
|
|
|
|
'dt=qca&'+
|
|
|
|
'dt=rw&'+
|
|
|
|
'dt=rm&'+
|
|
|
|
'dt=ss&'+
|
|
|
|
'dt=t&'+
|
|
|
|
'otf=1&'+
|
|
|
|
'ssel=0&'+
|
|
|
|
'tsel=0&'+
|
|
|
|
'kc=1&'+
|
|
|
|
`${token.name}=${token.value}`,
|
|
|
|
{form: {q: text}},
|
|
|
|
(error, response, body) => {
|
|
|
|
try {
|
|
|
|
body = JSON.parse(body);
|
|
|
|
} catch (e) {
|
2020-06-20 06:19:00 +00:00
|
|
|
return reject([response.statusCode, response.statusMessage, response.request.uri.href]);
|
2020-06-18 08:12:56 +00:00
|
|
|
}
|
|
|
|
resolve(get_first_while_array(body));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}, reject);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-06-20 20:42:50 +00:00
|
|
|
function try_to_translate(){
|
2020-06-21 00:17:05 +00:00
|
|
|
out_strings[0] = split_into_lines(assembled_input_string).splice(-3).join(' ');
|
|
|
|
var translation_candidate = out_strings[0].split(' ').splice(-translation_word_count).join(' ');
|
2020-06-20 20:42:50 +00:00
|
|
|
make_translations(translation_candidate);
|
|
|
|
}
|
|
|
|
|
2020-06-18 08:12:56 +00:00
|
|
|
function lock_translator_timedout(){
|
|
|
|
translator_locked = false;
|
2020-06-20 20:42:50 +00:00
|
|
|
if (translation_bounced_due_to_lock) {
|
|
|
|
translation_bounced_due_to_lock = false;
|
|
|
|
try_to_translate();
|
|
|
|
}
|
2020-06-18 08:12:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function lock_translator(){
|
|
|
|
translator_locked = true;
|
|
|
|
setTimeout(lock_translator_timedout, translation_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
function prune_translations() {
|
|
|
|
for (var k in last_translations)
|
|
|
|
while (last_translations[k].length > 2048)
|
|
|
|
last_translations[k].shift();
|
|
|
|
}
|
|
|
|
|
2020-06-21 00:17:05 +00:00
|
|
|
function reset_silence_timeout() {
|
|
|
|
if(silence_timeout_id!==null)
|
|
|
|
clearTimeout(silence_timeout_id);
|
|
|
|
silence_timeout_id = setTimeout(silence_timeout_exceeded_action, silence_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
function silence_timeout_exceeded_action() {
|
|
|
|
past_input_strings = [];
|
|
|
|
latest_input_string = '';
|
|
|
|
out_strings[0] = '';
|
|
|
|
out_strings[1] = '';
|
|
|
|
out_strings[2] = '';
|
|
|
|
}
|
|
|
|
|
2020-06-18 08:12:56 +00:00
|
|
|
function make_translations(text) {
|
|
|
|
prune_translations();
|
|
|
|
var translations_to_get = [];
|
|
|
|
for(var i = 1; i < lang_array.length; i++){
|
|
|
|
if (lang_array[i]==null)
|
|
|
|
out_strings[i] = '';
|
|
|
|
else if (lang_array[i]==lang_array[0])
|
|
|
|
out_strings[i] = out_strings[0];
|
|
|
|
else if (translations_to_get.indexOf(lang_array[i])==-1)
|
|
|
|
translations_to_get.push(lang_array[i]);
|
|
|
|
}
|
|
|
|
if (!translator_locked) {
|
|
|
|
lock_translator();
|
|
|
|
for (let translang of translations_to_get){
|
|
|
|
last_translations[translang] = last_translations[translang] || [];
|
|
|
|
cached = Object.fromEntries(last_translations[translang])[text];
|
|
|
|
if (cached) {
|
|
|
|
for(var j = 1; j < lang_array.length; j++){
|
|
|
|
let v = parseInt(j);
|
|
|
|
if (lang_array[v]==translang)
|
|
|
|
out_strings[v] = cached;
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-20 20:42:50 +00:00
|
|
|
translations_ordered += 1;
|
2020-06-18 08:12:56 +00:00
|
|
|
do_translate(text, lang_array[0], translang).then((translation) => {
|
|
|
|
last_translations[translang].push([text, translation]);
|
|
|
|
for(var k = 1; k < lang_array.length; k++){
|
|
|
|
let v = parseInt(k);
|
|
|
|
if (lang_array[v]==translang)
|
|
|
|
out_strings[v] = translation;
|
|
|
|
}
|
2020-06-20 09:50:52 +00:00
|
|
|
translations_received += 1;
|
2020-06-20 06:19:00 +00:00
|
|
|
}, err => console.log(`Erro ao traduzir para '${translang}': ${err}`));
|
2020-06-18 08:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-20 20:42:50 +00:00
|
|
|
} else
|
|
|
|
translation_bounced_due_to_lock = true;
|
2020-06-18 08:12:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function get_first_while_array(a) {
|
|
|
|
if (Array.isArray(a))
|
|
|
|
return get_first_while_array(a[0]);
|
|
|
|
else
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
server.get("/", (req, res) => {
|
|
|
|
res.writeHead(200, "OK", {'Content-Type': 'text/html'});
|
|
|
|
res.write(index_page);
|
|
|
|
res.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
server.get("/main.css", (req, res) => {
|
|
|
|
res.writeHead(200, "OK", {'Content-Type': 'text/css'});
|
|
|
|
res.write(main_style);
|
|
|
|
res.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
server.get("/main.js", (req, res) => {
|
|
|
|
res.writeHead(200, "OK", {'Content-Type': 'application/javascript'});
|
|
|
|
res.write(main_script);
|
|
|
|
res.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
server.post("/set/config", (req, res) => {
|
|
|
|
lang_array[1] = req.body.to_lang1;
|
|
|
|
lang_array[2] = req.body.to_lang2;
|
|
|
|
res.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
server.get("/get/speech", (req, res) => {
|
|
|
|
res.writeHead(200, "OK", {'Content-Type': 'application/json'});
|
2020-06-20 09:50:52 +00:00
|
|
|
res.write(JSON.stringify({
|
|
|
|
boxes: out_strings,
|
|
|
|
translation_count: translations_ordered,
|
|
|
|
translated_count: translations_received,
|
|
|
|
}));
|
2020-06-18 08:12:56 +00:00
|
|
|
res.end();
|
|
|
|
for (let i in lang_array) {
|
|
|
|
update_file(
|
2020-06-21 00:17:05 +00:00
|
|
|
'live_translator_caixa_'+box_names[i]+'.txt',
|
2020-06-18 08:12:56 +00:00
|
|
|
out_strings[i]);
|
|
|
|
}
|
2020-06-21 00:17:05 +00:00
|
|
|
for (let lang of [...(new Set(lang_array))]) {
|
2020-06-18 08:12:56 +00:00
|
|
|
let i = lang_array.indexOf(lang);
|
|
|
|
if (lang_array[i]===null) continue;
|
|
|
|
update_file(
|
2020-06-21 00:17:05 +00:00
|
|
|
'live_translator_idioma_'+lang+'.txt',
|
2020-06-18 08:12:56 +00:00
|
|
|
out_strings[i]);
|
|
|
|
}
|
2020-06-21 00:17:05 +00:00
|
|
|
for (let i in lang_array) {
|
|
|
|
update_file(
|
|
|
|
'live_translator_3linhas_caixa_'+box_names[i]+'.txt',
|
|
|
|
split_into_lines(out_strings[i]).splice(-3).join('\r\n'));
|
|
|
|
}
|
|
|
|
for (let i in lang_array) {
|
|
|
|
update_file(
|
|
|
|
'live_translator_2linhas_caixa_'+box_names[i]+'.txt',
|
|
|
|
split_into_lines(out_strings[i]).splice(-2).join('\r\n'));
|
|
|
|
}
|
|
|
|
for (let lang of [...(new Set(lang_array))]) {
|
|
|
|
let i = lang_array.indexOf(lang);
|
|
|
|
if (lang_array[i]===null) continue;
|
|
|
|
update_file(
|
|
|
|
'live_translator_3linhas_idioma_'+lang+'.txt',
|
|
|
|
split_into_lines(out_strings[i]).splice(-3).join('\r\n'));
|
|
|
|
}
|
|
|
|
for (let lang of [...(new Set(lang_array))]) {
|
|
|
|
let i = lang_array.indexOf(lang);
|
|
|
|
if (lang_array[i]===null) continue;
|
|
|
|
update_file(
|
|
|
|
'live_translator_2linhas_idioma_'+lang+'.txt',
|
|
|
|
split_into_lines(out_strings[i]).splice(-2).join('\r\n'));
|
|
|
|
}
|
2020-06-18 08:12:56 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
server.post("/set/speech", (req, res) => {
|
|
|
|
var speech = req.body;
|
|
|
|
res.end();
|
|
|
|
if (speech.lang != lang_array[0]) {
|
2020-06-20 19:39:22 +00:00
|
|
|
lang_array[0] = speech.lang;
|
2020-06-18 08:12:56 +00:00
|
|
|
last_translations = {};
|
|
|
|
out_strings[0] = '';
|
2020-06-21 00:17:05 +00:00
|
|
|
assembled_input_string = '';
|
2020-06-18 08:12:56 +00:00
|
|
|
past_input_strings = [];
|
|
|
|
latest_input_string = '';
|
|
|
|
latest_input_id = null;
|
|
|
|
}
|
|
|
|
if (speech.identity != latest_input_id) {
|
|
|
|
latest_input_id = speech.identity;
|
|
|
|
past_input_strings.push(latest_input_string);
|
2020-06-20 20:42:50 +00:00
|
|
|
if (past_input_strings.length > max_past_input_strings)
|
2020-06-18 08:12:56 +00:00
|
|
|
past_input_strings.shift();
|
|
|
|
latest_input_string = '';
|
|
|
|
}
|
|
|
|
var s = '';
|
|
|
|
for (var i = speech.resultIndex; i < speech.results.length; i++)
|
|
|
|
s += ' ' + speech.results[i].alternatives[0].transcript;
|
|
|
|
s = s.split(' ').filter(x=>x.length).join(' ');
|
|
|
|
latest_input_string = s;
|
2020-06-21 00:17:05 +00:00
|
|
|
reset_silence_timeout();
|
|
|
|
assembled_input_string = (
|
2020-06-18 08:12:56 +00:00
|
|
|
past_input_strings.join(' ')+' '+latest_input_string
|
|
|
|
).split(' ').filter(x=>x.length).join(' ');
|
2020-06-20 20:42:50 +00:00
|
|
|
try_to_translate();
|
2020-06-18 08:12:56 +00:00
|
|
|
});
|
|
|
|
|
2020-06-21 00:17:05 +00:00
|
|
|
function split_into_lines(text) {
|
|
|
|
var tokens = text.split(' ').flatMap(x=>[' ', x]).slice(1);
|
|
|
|
var lines = [];
|
|
|
|
var line = [];
|
|
|
|
for (let token of tokens) {
|
|
|
|
if ((token == ' ') && (line.length == 0))
|
|
|
|
continue;
|
|
|
|
let sizemapper = (wrd) => word_measurements[wrd] || 0;
|
|
|
|
let tokensize = sizemapper(token);
|
|
|
|
let linesize = line.map(sizemapper).reduce((a, b) => a + b, 0);
|
|
|
|
if (linesize+tokensize > current_box_width) {
|
|
|
|
lines.push(line);
|
|
|
|
line = [];
|
|
|
|
}
|
|
|
|
line.push(token);
|
|
|
|
}
|
|
|
|
lines.push(line);
|
|
|
|
for (let lnum in lines)
|
|
|
|
lines[lnum] = lines[lnum].join('').trim(' ');
|
|
|
|
lines = lines.filter(x=>x.length);
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
server.post("/set/measurements", (req, res) => {
|
|
|
|
var measurements = req.body;
|
|
|
|
res.end();
|
|
|
|
for (let word in measurements.wordWidths)
|
|
|
|
word_measurements[word] = measurements.wordWidths[word];
|
|
|
|
if (measurements.boxWidth)
|
|
|
|
current_box_width = measurements.boxWidth;
|
|
|
|
});
|
|
|
|
|
2020-06-20 09:50:52 +00:00
|
|
|
server.listen(13653, '127.0.0.1', ()=>{
|
|
|
|
console.log('Abra http://localhost:13653 no Chrome.');
|
|
|
|
});
|
2020-06-20 06:19:00 +00:00
|
|
|
|