447 lines
16 KiB
JavaScript
447 lines
16 KiB
JavaScript
/* global settings, resourcesPath, sound, twitch, getLanguageProperties, addSingleTooltip, showChatMessage, languageObject, addVoiceService, internalVoices, ttsRequestCount, main, path, pythonPath, settingsPath, ipcRenderer */
|
|
|
|
const spawn = require('child_process').spawn;
|
|
const kill = require('kill-process-by-name');
|
|
let python;
|
|
|
|
async function getInstalledVoices() {
|
|
if (!settings.TTS.USE_TTS) {
|
|
return;
|
|
}
|
|
addVoiceService('Internal');
|
|
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/voices`, { method: 'GET' });
|
|
if (response.ok) {
|
|
const responseData = await response.json();
|
|
console.log('Voices:', responseData);
|
|
internalVoices = responseData;
|
|
} else {
|
|
console.error('Failed to send termination signal to Flask server.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
}
|
|
|
|
const primaryVoice = document.querySelector('#primaryVoice');
|
|
const secondaryVoice = document.querySelector('#secondaryVoice');
|
|
|
|
function setVoicesinSelect(voiceSelect) {
|
|
const voices = Object.values(internalVoices.voices);
|
|
voices.forEach(voice => {
|
|
const option = document.createElement('option');
|
|
option.classList.add('option');
|
|
|
|
option.value = voice;
|
|
option.innerHTML = voice;
|
|
|
|
voiceSelect.appendChild(option);
|
|
});
|
|
}
|
|
setVoicesinSelect(primaryVoice);
|
|
primaryVoice.value = settings.TTS.PRIMARY_VOICE;
|
|
setVoicesinSelect(secondaryVoice);
|
|
secondaryVoice.value = settings.TTS.SECONDARY_VOICE;
|
|
}
|
|
|
|
// TODO: refactor
|
|
function setTranslatedUserMessage(message) {
|
|
const userMessage = document.getElementById(message.messageId);
|
|
const messageBox = userMessage.getElementsByClassName('msg-box')[0];
|
|
|
|
const languageElement = document.createElement('span');
|
|
languageElement.classList = `fi fi-${message.language.selectedLanguage.ISO3166} fis flag-icon user-flag`;
|
|
languageElement.setAttribute('tip', message.language.selectedLanguage.name);
|
|
userMessage.appendChild(languageElement);
|
|
addSingleTooltip(languageElement);
|
|
|
|
const translationHeader = document.createElement('div');
|
|
translationHeader.className = 'translation-header user';
|
|
translationHeader.innerText = 'Translation';
|
|
messageBox.appendChild(translationHeader);
|
|
|
|
const languageElement2 = document.createElement('span');
|
|
languageElement2.classList = `fi fi-${message.language.detectedLanguage.ISO3166} fis flag-icon user`;
|
|
languageElement2.setAttribute('tip', message.language.detectedLanguage.name);
|
|
addSingleTooltip(languageElement2);
|
|
messageBox.appendChild(languageElement2);
|
|
|
|
const translationMessage = document.createElement('div');
|
|
translationMessage.className = 'translation-message user';
|
|
translationMessage.innerText = message.translation;
|
|
messageBox.appendChild(translationMessage);
|
|
}
|
|
|
|
function setTranslatedMessage(message) {
|
|
// this determines if it is a message that is send by a user
|
|
const languageBox = document.getElementById(message.messageId).getElementsByClassName('language-icon flag-icon')[0];
|
|
// if (false) {
|
|
// twitch.sendMessage(
|
|
// `[${message.language.detectedLanguage.name} ${message.language.detectedLanguage.ISO639} > ${message.language.selectedLanguage.name} ${message.language.selectedLanguage.ISO639}] @${settings.TWITCH.USERNAME}: ${message.translation}`
|
|
// );
|
|
// return setTranslatedUserMessage(message);
|
|
// }
|
|
|
|
if (message.language.selectedLanguage.ISO639 !== message.language.detectedLanguage.ISO639) {
|
|
const messageBox = document.getElementById(message.messageId).getElementsByClassName('msg-box')[0];
|
|
|
|
languageBox.classList = `fi fi-${message.language.detectedLanguage.ISO3166} fis language-icon flag-icon`;
|
|
languageBox.setAttribute('tip', message.language.detectedLanguage.name);
|
|
|
|
const translationHeader = document.createElement('div');
|
|
translationHeader.className = 'translation-header';
|
|
translationHeader.innerText = 'Translation';
|
|
messageBox.appendChild(translationHeader);
|
|
|
|
const translationIcon = document.createElement('div');
|
|
translationIcon.className = 'translation-icon';
|
|
const languageElement = document.createElement('span');
|
|
languageElement.classList = `fi fi-${message.language.selectedLanguage.ISO3166} fis flag-icon`;
|
|
languageElement.setAttribute('tip', message.language.selectedLanguage.name);
|
|
addSingleTooltip(languageElement);
|
|
translationIcon.appendChild(languageElement);
|
|
messageBox.appendChild(translationIcon);
|
|
|
|
const translationMessage = document.createElement('div');
|
|
translationMessage.className = 'translation-message';
|
|
translationMessage.innerText = message.translation;
|
|
messageBox.appendChild(translationMessage);
|
|
// showChatMessage();
|
|
const messages = document.body.querySelectorAll('.msg-container');
|
|
|
|
const lastMessage = messages[messages.length - 1];
|
|
lastMessage.scrollIntoView({ block: 'end', behavior: 'smooth' });
|
|
}
|
|
|
|
console.log(message);
|
|
if (settings.LANGUAGE.OUTPUT_TO_TTS) {
|
|
sound.playVoice({
|
|
originalMessage: message.originalMessage,
|
|
filteredMessage: message.translation,
|
|
logoUrl: message.logoUrl,
|
|
username: message.username,
|
|
formattedMessage: message.formattedMessage,
|
|
language: message.language
|
|
});
|
|
}
|
|
return message.language.detectedLanguage;
|
|
}
|
|
|
|
async function getTranslatedMessage(message) {
|
|
// TODO: translate primary language to
|
|
console.log(message);
|
|
console.log(message.isPrimaryLanguage ? message.language.selectedLanguage.IETF : message.language.detectedLanguage.IETF);
|
|
const requestOptions = {
|
|
method: 'POST', // HTTP method
|
|
headers: {
|
|
'Content-Type': 'application/json; charset="utf-8"' // Specify the content type
|
|
},
|
|
body: JSON.stringify({
|
|
message: message.message,
|
|
language: message.isPrimaryLanguage ? message.language.selectedLanguage.IETF : message.language.detectedLanguage.IETF
|
|
}) // Convert the data to JSON and include it in the request body
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/translate`, requestOptions);
|
|
const responseData = await response.json();
|
|
if (response.ok) {
|
|
console.log('Translated message:', responseData);
|
|
|
|
if (settings.LANGUAGE.BROADCAST_TRANSLATION) {
|
|
twitch.sendMessage(
|
|
`[${message.language.detectedLanguage.name} ${message.language.detectedLanguage.ISO639} > ${message.language.selectedLanguage.name} ${message.language.selectedLanguage.ISO639}] @${message.username}: ${responseData.translation}`
|
|
);
|
|
}
|
|
setTranslatedMessage({
|
|
originalMessage: message.message,
|
|
translation: responseData.translation,
|
|
messageId: message.messageId,
|
|
language: message.language,
|
|
formattedMessage: message.formattedMessage,
|
|
username: message.username,
|
|
logoUrl: message.logoUrl
|
|
});
|
|
return message.language.detectedLanguage;
|
|
} else {
|
|
console.error(responseData);
|
|
if (responseData.code === 500) {
|
|
if (message.remainingDetectedLanguages.length > 0) {
|
|
message.language.detectedLanguage = getLanguageProperties(message.remainingDetectedLanguages[0]);
|
|
message.remainingDetectedLanguages.shift();
|
|
return getTranslatedMessage(message);
|
|
} else {
|
|
message.message = 'Error, Could not translate message';
|
|
message.language.detectedLanguage = getLanguageProperties('en-GB');
|
|
return getTranslatedMessage(message);
|
|
}
|
|
}
|
|
if (responseData.code === 429) {
|
|
message.language.detectedLanguage = getLanguageProperties('en-GB');
|
|
setTranslatedMessage({
|
|
originalMessage: message.message,
|
|
translation: 'Rate limit exceeded, please change translation service.',
|
|
messageId: message.messageId,
|
|
language: message.language,
|
|
formattedMessage: message.formattedMessage,
|
|
username: message.username,
|
|
logoUrl: message.logoUrl
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
message.language.detectedLanguage = getLanguageProperties('en-GB');
|
|
setTranslatedMessage({
|
|
originalMessage: message.message,
|
|
translation: 'Error, could not translate message.',
|
|
messageId: message.messageId,
|
|
language: message.language,
|
|
formattedMessage: message.formattedMessage,
|
|
username: message.username,
|
|
logoUrl: message.logoUrl
|
|
});
|
|
}
|
|
}
|
|
|
|
async function filterLanguage(message) {
|
|
const selectedPrimaryLanguage = getLanguageProperties(settings.LANGUAGE.TRANSLATE_TO);
|
|
const selectedPrimaryLanguageIndex =
|
|
message.languages.indexOf(selectedPrimaryLanguage.ISO639) === -1 ? 99 : message.languages.indexOf(selectedPrimaryLanguage.ISO639);
|
|
|
|
const selectedSecondaryLanguage = getLanguageProperties(settings.TTS.SECONDARY_TTS_LANGUAGE);
|
|
const selectedSecondaryLanguageIndex =
|
|
message.languages.indexOf(selectedSecondaryLanguage.ISO639) === -1 ? 99 : message.languages.indexOf(selectedSecondaryLanguage.ISO639);
|
|
|
|
let detectedLanguage = '';
|
|
const remainingDetectedLanguages = [];
|
|
const detectedLanguages = message.languages.slice();
|
|
|
|
for (const [index, language] of detectedLanguages.entries()) {
|
|
detectedLanguage = getLanguageProperties(language);
|
|
|
|
if (detectedLanguage !== 'error') {
|
|
detectedLanguages.splice(index, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const [index, language] of detectedLanguages.entries()) {
|
|
const remainderLanguage = getLanguageProperties(language);
|
|
if (remainderLanguage !== 'error') {
|
|
remainingDetectedLanguages.push(remainderLanguage.IETF);
|
|
}
|
|
}
|
|
|
|
const language = selectedPrimaryLanguageIndex < selectedSecondaryLanguageIndex ? selectedPrimaryLanguage : detectedLanguage;
|
|
|
|
if (settings.LANGUAGE.TRANSLATE_TO !== 'none' && selectedPrimaryLanguage.ISO639 !== language.ISO639) {
|
|
// console.log('1');
|
|
console.log('hola');
|
|
getTranslatedMessage({
|
|
message: message.message,
|
|
messageId: message.messageId,
|
|
remainingDetectedLanguages,
|
|
isPrimaryLanguage: false,
|
|
language: {
|
|
selectedLanguage: selectedPrimaryLanguage,
|
|
detectedLanguage: detectedLanguage
|
|
},
|
|
username: message.username,
|
|
formattedMessage: message.formattedMessage,
|
|
logoUrl: message.logoUrl
|
|
});
|
|
} else {
|
|
console.log('adios');
|
|
// console.log('2');
|
|
setTranslatedMessage({
|
|
originalMessage: message.message,
|
|
translation: message.message,
|
|
messageId: message.messageId,
|
|
language: {
|
|
selectedLanguage: selectedPrimaryLanguage,
|
|
detectedLanguage: selectedPrimaryLanguage
|
|
},
|
|
formattedMessage: message.formattedMessage,
|
|
username: message.username,
|
|
logoUrl: message.logoUrl
|
|
});
|
|
// getTranslatedMessage({
|
|
// message: message.message,
|
|
// messageId: message.messageId,
|
|
// remainingDetectedLanguages: [],
|
|
// isPrimaryLanguage: true,
|
|
// language: {
|
|
// selectedLanguage: selectedSecondaryLanguage,
|
|
// detectedLanguage: selectedPrimaryLanguage
|
|
// },
|
|
// username: message.username,
|
|
// formattedMessage: message.formattedMessage,
|
|
// logoUrl: message.logoUrl
|
|
// });
|
|
}
|
|
|
|
return language;
|
|
}
|
|
|
|
async function getDetectedLanguage(message) {
|
|
if (!settings.LANGUAGE.USE_DETECTION) {
|
|
return;
|
|
}
|
|
|
|
const requestOptions = {
|
|
method: 'POST', // HTTP method
|
|
headers: {
|
|
'Content-Type': 'application/json' // Specify the content type
|
|
},
|
|
body: JSON.stringify({ message: message.message }) // Convert the data to JSON and include it in the request body
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/detect`, requestOptions);
|
|
if (response.ok) {
|
|
const responseData = await response.json();
|
|
|
|
console.log('Detected Languages:', responseData);
|
|
return await filterLanguage({
|
|
languages: responseData.languages,
|
|
message: message.message,
|
|
messageId: message.messageId,
|
|
username: message.username,
|
|
formattedMessage: message.formattedMessage
|
|
});
|
|
} else {
|
|
console.error('Failed to send termination signal to Flask server.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
}
|
|
}
|
|
|
|
async function getBackendServerStatus() {
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/status`, { method: 'GET' });
|
|
if (response.ok) {
|
|
const responseData = await response.json();
|
|
console.log('Status:', responseData);
|
|
} else {
|
|
console.error('Failed to send termination signal to Flask server.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
}
|
|
}
|
|
|
|
function startSTT() {
|
|
const eventSource = new EventSource('http://127.0.0.1:9000/stream');
|
|
|
|
eventSource.addEventListener('message', event => {
|
|
const result = event.data;
|
|
console.log(result); // Log the received data
|
|
});
|
|
|
|
eventSource.addEventListener('error', event => {
|
|
console.error('EventSource failed:', event);
|
|
|
|
eventSource.close();
|
|
});
|
|
|
|
window.addEventListener('beforeunload', () => {
|
|
eventSource.close();
|
|
});
|
|
}
|
|
|
|
async function getInternalTTSAudio(requestData) {
|
|
ttsRequestCount++;
|
|
requestData.count = ttsRequestCount;
|
|
const requestOptions = {
|
|
method: 'POST', // HTTP method
|
|
headers: {
|
|
'Content-Type': 'application/json' // Specify the content type
|
|
},
|
|
body: JSON.stringify(requestData) // Convert the data to JSON and include it in the request body
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/audio`, requestOptions);
|
|
if (response.ok) {
|
|
const responseData = await response.json();
|
|
console.log('Audio:', responseData);
|
|
return ttsRequestCount;
|
|
} else {
|
|
console.error('Failed to send termination signal to Flask server.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
}
|
|
}
|
|
|
|
const createBackendServer = () =>
|
|
new Promise(resolve => {
|
|
if (main.isPackaged) {
|
|
python = spawn(path.join(pythonPath, './loquendoBot_backend.exe'), [settingsPath, 'prod']);
|
|
} else {
|
|
python = spawn('python', ['-u', path.join(resourcesPath, '../backend/loquendoBot_backend.py'), settingsPath, 'dev']);
|
|
}
|
|
// Capture the stdout of the Python process
|
|
python.stdout.on('data', data => {
|
|
console.info(`${data}`);
|
|
});
|
|
|
|
// Capture the stderr of the Python process
|
|
python.stderr.on('data', data => {
|
|
// console.error(`${data}`);
|
|
if (data.toString().startsWith('INFO:waitress:Serving on')) {
|
|
resolve('finished');
|
|
} else {
|
|
console.error(`${data}`);
|
|
}
|
|
});
|
|
|
|
// Listen for the Python process to exit
|
|
python.on('close', code => {
|
|
console.log(`Python process exited with code ${code}`);
|
|
});
|
|
|
|
if (typeof python.pid !== 'number') {
|
|
console.log('failed');
|
|
} else {
|
|
// console.log(`Spawned subprocess correctly!, PID = ${python.pid}`);
|
|
}
|
|
});
|
|
|
|
async function initiateBackend() {
|
|
try {
|
|
createBackendServer().then(() => {
|
|
getBackendServerStatus();
|
|
getInstalledVoices();
|
|
if (settings.STT.USE_STT && !settings.STT.LANGUAGE === '') {
|
|
startSTT();
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error during backend initialization:', error);
|
|
}
|
|
}
|
|
|
|
initiateBackend();
|
|
|
|
// TODO: convert to restartServer function
|
|
ipcRenderer.on('quit-event', async () => {
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${settings.GENERAL.PORT}/terminate`, { method: 'GET' });
|
|
if (response.ok) {
|
|
const responseData = await response.json();
|
|
console.log('Response:', responseData);
|
|
kill('loquendoBot_backend');
|
|
} else {
|
|
console.error('Failed to send termination signal to Flask server.');
|
|
kill('loquendoBot_backend');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending termination signal:', error);
|
|
kill('loquendoBot_backend');
|
|
}
|
|
});
|
|
|
|
module.exports = { getInternalTTSAudio, getDetectedLanguage, getTranslatedMessage };
|