LoquendoBot/src/js/renderer.js
2024-10-13 22:21:59 +02:00

579 lines
20 KiB
JavaScript

/* eslint-disable no-unused-vars */
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const Sockette = require('sockette');
const { webFrame, ipcRenderer, shell } = require('electron');
const io = require('socket.io-client');
const GoogleTTS = require('node-google-tts-api');
const tts = new GoogleTTS();
const { Socket } = require('socket.io-client');
const main = ipcRenderer.sendSync('environment');
const resourcesPath = main.resourcesPath;
const settingsPath = main.settingsPath.toString();
const pythonPath = main.pythonPath.toString();
const settings = main.settings;
// TODO: remove gooogle voices txt and use api instead
const googleVoices = fs.readFileSync(path.join(__dirname, './config/googleVoices.txt')).toString().split('\r\n');
// TODO: remove amazon voices txt and use api instead (sakura project has it)
const amazonVoices = fs.readFileSync(path.join(__dirname, './config/amazonVoices.txt')).toString().split('\r\n');
const customEmotesListSavePath =
main.isPackaged === true ? path.join(resourcesPath, './custom-emotes.json') : path.join(resourcesPath, './config/custom-emotes.json');
// html elements
const root = document.documentElement;
const ttsSelector = document.body.querySelector('#TTSSelector');
const amazonVoiceSelect = document.querySelector('#amazonVoice'); // obtain the html reference of the amazon voices comboBox
const notificationAudioDevices = document.querySelector('#notificationAudioDevice'); // obtain the html reference of the installedTTS comboBox
const devicesDropdown = document.querySelector('#devicesDropdown');
const notificationSound = document.querySelector('#notification'); // obtain the html reference of the sound comboBox
const sttModel = document.querySelector('#sttModel'); // obtain the html reference of the sound comboBox
const ttsAudioDevices = document.querySelector('#ttsAudioDevice'); // obtain the html reference of the installedTTS comboBox
const notificationSoundAudioDevices = document.querySelector('#notificationSoundAudioDevice'); // obtain the html reference of the installedTTS comboBox
const emojiPicker = document.body.querySelector('emoji-picker');
const lol = document.body.querySelector('country-flag-emoji-polyfill');
// laod local javascript files
const chat = require(path.join(__dirname, './js/chat'));
const messageTemplates = require(path.join(__dirname, './js/messageTemplates'));
const languageObject = require(path.join(__dirname, './js/languages'));
const logger = require(path.join(__dirname, './js/logger'));
const sound = require(path.join(__dirname, './js/sound'));
const config = require(path.join(__dirname, './js/settings'));
const { TokenAutocomplete } = require(path.join(__dirname, './js/token-autocomplete'));
const betterTTVAutocomplete = new TokenAutocomplete({
name: 'sample',
selector: '#sample',
noMatchesText: 'No matching results...',
initialTokens: [...settings.TWITCH.BETTERTTV_CHANNELS]
});
const test = settings.TWITCH.BETTERTTV_CHANNELS;
console.log(test);
const mediaDevices = require(path.join(__dirname, './js/mediaDevices'));
const notificationSounds = path.join(resourcesPath, main.isPackaged ? './sounds/notifications' : '../sounds/notifications');
const sttModels = path.join(resourcesPath, main.isPackaged ? './speech_to_text_models' : '../speech_to_text_models');
function reset() {
ipcRenderer.send('restart');
}
const server = require(path.join(__dirname, './js/server'));
const backend = require(path.join(__dirname, './js/backend'));
const socket = io(`http://localhost:${settings.GENERAL.PORT}`); // Connect to your Socket.IO server
let twitch = null;
twitch = settings.TWITCH.USE_TWITCH ? require(path.join(__dirname, './js/twitch')) : '';
let dlive = null;
dlive = settings.DLIVE.USE_DLIVE ? require(path.join(__dirname, './js/dlive')) : '';
let youtube = null;
youtube = settings.YOUTUBE.USE_YOUTUBE ? require(path.join(__dirname, './js/youtube')) : '';
let trovo = null;
trovo = settings.TROVO.USE_TROVO ? require(path.join(__dirname, './js/trovo')) : '';
const Polly = settings.AMAZON.USE_AMAZON ? require(path.join(__dirname, './js/amazon')) : '';
const google = settings.GOOGLE.USE_GOOGLE ? require(path.join(__dirname, './js/google')) : '';
const theme = require(path.join(__dirname, './js/theme'));
const auth = require(path.join(__dirname, './js/auth'));
let ttsRequestCount = 0;
ttsRequestCount = 0;
let customEmojis = [];
customEmojis = [];
let messageId = 0;
messageId = 0;
let customEmojiList = [];
// initialize values
config.getGeneralSettings();
const TTSVolume = 1;
const notificationSoundVolume = 1;
const StartDateAndTime = Date.now();
const speakButton = document.querySelector('#speakBtn');
const amazonCredentials = {
accessKeyId: settings.AMAZON.ACCESS_KEY,
secretAccessKey: settings.AMAZON.ACCESS_SECRET
};
// Check for installed sounds
fs.readdir(notificationSounds, (err, files) => {
if (err) {
console.error(err);
}
files.forEach((file, i) => {
// Create a new option element.
const option = document.createElement('option');
// Set the options value and text.
option.value = i;
option.innerHTML = file;
// Add the option to the sound selector.
notificationSound.appendChild(option);
});
// set the saved notification sound
notificationSound.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND;
});
// Check for installed stt models
fs.readdir(sttModels, (err, files) => {
if (err) {
console.error(err);
}
for (const file of files) {
if (file.includes('.txt')) {
continue;
}
// Create a new option element.
const option = document.createElement('option');
// Set the options value and text.
option.value = file;
option.innerHTML = file;
// Add the option to the sound selector.
sttModel.appendChild(option);
}
// set the saved notification sound
sttModel.value = settings.STT.LANGUAGE;
});
// TODO: refactor obtaining audio devices.
async function getAudioDevices() {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
return;
}
const devices = await navigator.mediaDevices.enumerateDevices();
const audioOutputDevices = devices.filter(device => device.kind === 'audiooutput');
audioOutputDevices.forEach(device => {
const option1 = document.createElement('option');
const option2 = document.createElement('option');
option1.text = device.label || `Output ${device.deviceId}`;
option2.text = device.label || `Output ${device.deviceId}`;
option1.value = device.deviceId;
option2.value = device.deviceId;
ttsAudioDevices.appendChild(option1);
notificationSoundAudioDevices.appendChild(option2);
});
ttsAudioDevices.selectedIndex = settings.AUDIO.SELECTED_TTS_AUDIO_DEVICE;
notificationSoundAudioDevices.selectedIndex = settings.AUDIO.SELECTED_NOTIFICATION_AUDIO_DEVICE;
}
getAudioDevices();
function setSelectedLanguageinSelect(languageSelect, language) {
const button = languageSelect.querySelector('.SmallButton');
const languageElement = document.createElement('span');
languageElement.classList = `fi fi-${language.ISO3166} fis pop-selection`;
languageElement.setAttribute('tip', language.name);
button.innerHTML = '';
button.appendChild(languageElement);
addSingleTooltip(languageElement);
}
function setLanguagesinSelectx(languageSelector, language) {
const languageSelect = document.querySelector(languageSelector); // obtain the html reference of the google voices comboBox
const languageSelectContent = languageSelect.querySelector('.pop-content');
languageSelectContent.addEventListener('click', e => {
const parent = e.target.parentElement.id;
language = getLanguageProperties(e.target.getAttribute('value'));
if (parent === 'SEND_TRANSLATION_IN') {
settings.LANGUAGE.SEND_TRANSLATION_IN = language.IETF;
} else {
settings.LANGUAGE.SEND_TRANSLATION_OUT = language.IETF;
}
fs.writeFileSync(settingsPath, JSON.stringify(settings));
setSelectedLanguageinSelect(languageSelect, language);
});
for (const language in languageObject.languages) {
if (Object.prototype.hasOwnProperty.call(languageObject.languages, language)) {
const IETF = languageObject.languages[language].IETF;
const ISO639 = languageObject.languages[language].ISO639;
const ISO3166 = languageObject.languages[language].ISO3166;
const option = document.createElement('div');
option.classList = 'language-select';
const languageElement = document.createElement('span');
languageElement.classList = `fi fi-${ISO3166} fis`;
languageElement.style.pointerEvents = 'none';
option.setAttribute('tip', language);
const text = document.createElement('span');
text.style.pointerEvents = 'none';
text.innerHTML = ` - ${ISO639}`;
option.setAttribute('value', IETF);
languageSelectContent.appendChild(option);
option.appendChild(languageElement);
option.appendChild(text);
addSingleTooltip(option);
}
}
setSelectedLanguageinSelect(languageSelect, language);
}
setLanguagesinSelectx('.pop.in', getLanguageProperties(settings.LANGUAGE.SEND_TRANSLATION_IN));
setLanguagesinSelectx('.pop.out', getLanguageProperties(settings.LANGUAGE.SEND_TRANSLATION_OUT));
function setLanguagesinSelect(languageSelector, setting) {
const languageSelect = document.querySelector(languageSelector); // obtain the html reference of the google voices comboBox
for (const language in languageObject.languages) {
if (Object.prototype.hasOwnProperty.call(languageObject.languages, language)) {
const IETF = languageObject.languages[language].IETF;
const ISO639 = languageObject.languages[language].ISO639;
const option = document.createElement('option');
option.value = IETF;
option.innerHTML = `${ISO639} : ${language}`;
languageSelect.appendChild(option);
}
}
languageSelect.selectedIndex = setting;
}
setLanguagesinSelect('#language', settings.GENERAL.LANGUAGE_INDEX);
setLanguagesinSelect('#defaultLanguage', settings.TTS.PRIMARY_TTS_LANGUAGE_INDEX);
setLanguagesinSelect('#secondaryLanguage', settings.TTS.SECONDARY_TTS_LANGUAGE_INDEX);
setLanguagesinSelect('#TRANSLATE_TO', settings.LANGUAGE.TRANSLATE_TO_INDEX);
function addVoiceService(name) {
function addToselect(select) {
const ttsService = document.querySelector(select);
const option = document.createElement('option');
ttsService.appendChild(option);
option.value = name;
option.innerHTML = name;
}
addToselect('#primaryTTSService');
addToselect('#secondaryTTSService');
}
function determineTootlTipPosition(element) {
const horizontal = document.body.clientWidth / 2;
const vertical = document.body.clientHeight / 2;
element.tip.style.left = `${element.mouse.x}px`;
element.tip.style.top = `${element.mouse.y}px`;
const tipPosition = element.tip.getBoundingClientRect();
if (element.position.x < horizontal && element.position.y < vertical) {
element.tip.style.top = `${parseInt(element.tip.style.top) + 25}px`;
element.tip.style.left = `${parseInt(element.tip.style.left) + 10}px`;
}
if (element.position.x < horizontal && element.position.y > vertical) {
element.tip.style.top = `${parseInt(element.tip.style.top) - tipPosition.height}px`;
element.tip.style.left = `${parseInt(element.tip.style.left) + 10}px`;
}
if (element.position.x > horizontal && element.position.y < vertical) {
element.tip.style.top = `${parseInt(element.tip.style.top) + 25}px`;
element.tip.style.left = `${parseInt(element.tip.style.left) - tipPosition.width}px`;
}
if (element.position.x > horizontal && element.position.y > vertical) {
element.tip.style.top = `${parseInt(element.tip.style.top) - tipPosition.height}px`;
element.tip.style.left = `${parseInt(element.tip.style.left) - tipPosition.width}px`;
}
element.tip.style.visibility = 'visible';
}
// Small tooltip
function addSingleTooltip(el) {
const tip = document.createElement('div');
const body = document.querySelector('.container');
const element = el;
tip.classList.add('tooltip');
tip.innerText = el.getAttribute('tip');
if (el.src) {
const image = document.createElement('img');
image.src = el.src;
tip.appendChild(image);
}
body.appendChild(tip);
tip.pointerEvents = 'none';
element.onmousemove = e => {
determineTootlTipPosition({
position: element.getBoundingClientRect(),
mouse: { x: e.x, y: e.y },
tip
});
};
element.onmouseleave = e => {
tip.style.visibility = 'hidden';
};
}
Array.from(document.body.querySelectorAll('[tip]')).forEach(el => {
addSingleTooltip(el);
});
function showChatMessage(article) {
let body = null;
if (article !== undefined) {
body = document.getElementById('chatBox');
body.appendChild(article);
}
const messages = document.body.querySelectorAll('.msg-container');
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ block: 'end', behavior: 'smooth' });
// const messageId = article.id;
// languageElement.setAttribute('id', article.id);
// console.log(article);
const username = article.querySelector('.username').innerHTML;
const image = article.querySelector('.user-img').src;
const statusCircle = article.querySelector('.status-circle').src;
const message = article.querySelector('.msg-box').innerHTML;
const postTime = article.querySelector('.post-time').innerHTML;
socket.emit('chat-out', { messageId, username, image, statusCircle, message, postTime });
}
function getPostTime() {
const date = new Date();
document.body.querySelectorAll('.container').innerHTML = date.getHours();
const hours = date.getHours();
const ampm = hours >= 12 ? 'PM' : 'AM';
const minutes = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes();
const time = `${hours}:${minutes} ${ampm}`;
return time;
}
function showPreviewChatMessage() {
const message = messageTemplates.messageTemplate;
document.querySelector('#mini-mid').innerHTML += message;
const messages = Array.from(document.body.querySelectorAll('#mini-mid'));
const lastMessage = messages[messages.length - 1];
lastMessage.scrollIntoView({ behavior: 'smooth' });
}
showPreviewChatMessage();
function hideText(button, field) {
document.body.querySelector(button).addEventListener('click', () => {
const passwordInput = document.querySelector(field);
if (passwordInput.type === 'password') {
passwordInput.type = 'lol';
} else {
passwordInput.type = 'password';
}
});
}
hideText('.password-toggle-btn1', '#TWITCH_OAUTH_TOKEN');
hideText('.password-toggle-btn2', '#TROVO_OAUTH_TOKEN');
hideText('.password-toggle-btn4', '#AMAZON_ACCESS_KEY');
hideText('.password-toggle-btn5', '#AMAZON_ACCESS_SECRET');
hideText('.password-toggle-btn6', '#GOOGLE_API_KEY');
function setZoomLevel(currentZoom, zoomIn) {
let newZoom = currentZoom.toFixed(2);
if (zoomIn === true && currentZoom < 4.95) {
newZoom = (currentZoom + 0.05).toFixed(2);
}
if (zoomIn === false && currentZoom > 0.25) {
newZoom = (currentZoom - 0.05).toFixed(2);
}
webFrame.setZoomFactor(parseFloat(newZoom));
settings.GENERAL.ZOOMLEVEL = newZoom;
fs.writeFileSync(settingsPath, JSON.stringify(settings));
document.body.querySelector('#ZOOMLEVEL').value = (settings.GENERAL.ZOOMLEVEL * 100).toFixed(0);
}
function getLanguageProperties(languageToDetect) {
try {
const filteredLanguage = Object.keys(languageObject.languages).reduce(function (accumulator, currentValue) {
if (
languageObject.languages[currentValue].IETF === languageToDetect ||
languageObject.languages[currentValue].ISO639 === languageToDetect ||
languageObject.languages[currentValue].ISO3166 === languageToDetect
) {
accumulator[currentValue] = languageObject.languages[currentValue];
}
return accumulator;
}, {});
const language = {
name: Object.getOwnPropertyNames(filteredLanguage)[0],
ISO3166: filteredLanguage[Object.keys(filteredLanguage)[0]].ISO3166,
ISO639: filteredLanguage[Object.keys(filteredLanguage)[0]].ISO639,
IETF: filteredLanguage[Object.keys(filteredLanguage)[0]].IETF
};
return language;
} catch (e) {
// console.error(error);
return 'error';
}
}
let customEmotes = null;
if (fs.existsSync(customEmotesListSavePath)) {
const file = fs.readFileSync(customEmotesListSavePath);
customEmotes = JSON.parse(file);
customEmojiList = [...customEmojiList, ...customEmotes];
}
emojiPicker.customEmoji = customEmojiList;
function saveCustomEmotesToFile(emotes) {
const data = JSON.stringify(emotes);
customEmojiList = [...customEmojiList, data];
const savePath =
main.isPackaged === true ? path.join(resourcesPath, './custom-emotes.json') : path.join(resourcesPath, './config/custom-emotes.json');
fs.writeFile(savePath, data, error => {
if (error) {
console.error(error);
throw error;
}
});
}
function countWords(str) {
return str.trim().split(/\s+/).length;
}
// function saveSettingsToFile(settings) {
// const data = JSON.stringify(settings);
// const savePath =
// main.isPackaged === true ? path.join(resourcesPath, './settings.json') : path.join(resourcesPath, './config/settings.json');
// fs.writeFile(savePath, data, error => {
// if (error) {
// console.error(error);
// throw error;
// }
// });
// }
/*
Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// var videoElement = document.querySelector('video');
// var audioSelect = document.querySelector('select#audioSource');
// var videoSelect = document.querySelector('select#videoSource');
// audioSelect.onchange = getStream;
// videoSelect.onchange = getStream;
// getStream().then(getDevices).then(gotDevices);
// function getDevices() {
// // AFAICT in Safari this only gets default devices until gUM is called :/
// return navigator.mediaDevices.enumerateDevices();
// }
// function gotDevices(deviceInfos) {
// window.deviceInfos = deviceInfos; // make available to console
// console.log('Available input and output devices:', deviceInfos);
// for (const deviceInfo of deviceInfos) {
// const option = document.createElement('option');
// option.value = deviceInfo.deviceId;
// if (deviceInfo.kind === 'audioinput') {
// option.text = deviceInfo.label || `Microphone ${audioSelect.length + 1}`;
// audioSelect.appendChild(option);
// } else if (deviceInfo.kind === 'videoinput') {
// option.text = deviceInfo.label || `Camera ${videoSelect.length + 1}`;
// videoSelect.appendChild(option);
// }
// }
// }
// function getStream() {
// if (window.stream) {
// window.stream.getTracks().forEach(track => {
// track.stop();
// });
// }
// const audioSource = audioSelect.value;
// const videoSource = videoSelect.value;
// const constraints = {
// audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
// video: { deviceId: videoSource ? { exact: videoSource } : undefined }
// };
// return navigator.mediaDevices.getUserMedia(constraints).then(gotStream).catch(handleError);
// }
// function gotStream(stream) {
// window.stream = stream; // make stream available to console
// audioSelect.selectedIndex = [...audioSelect.options].findIndex(option => option.text === stream.getAudioTracks()[0].label);
// videoSelect.selectedIndex = [...videoSelect.options].findIndex(option => option.text === stream.getVideoTracks()[0].label);
// videoElement.srcObject = stream;
// }
// function handleError(error) {
// console.error('Error: ', error);
// }
// const settingxs = {
// video: true
// };
// navigator.mediaDevices
// .getUserMedia(settingxs)
// .then(stream => {
// const video = document.getElementById('video');
// video.srcObject = stream;
// video.play();
// })
// .catch(err => {
// console.log(err);
// alert('Não há permissões para acessar a webcam.');
// });