42
.compilerc
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"development": {
|
||||
"application/javascript": {
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"electron": "8.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"react"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-async-to-generator"
|
||||
],
|
||||
"sourceMaps": "inline"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"application/javascript": {
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"electron": "8.2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"react"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-async-to-generator"
|
||||
],
|
||||
"sourceMaps": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb-base"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"linebreak-style":"off",
|
||||
"indent":["error", "tab"],
|
||||
"no-tabs":"off",
|
||||
"prefer-destructuring": ["error", {
|
||||
"AssignmentExpression": {
|
||||
"array": false,
|
||||
"object": true
|
||||
}
|
||||
}],
|
||||
"no-console": ["error", { "allow": ["warn", "error"] }]
|
||||
}
|
||||
}
|
||||
11
.gitignore
vendored
|
|
@ -93,12 +93,15 @@ out/
|
|||
|
||||
#custom files
|
||||
src/config/settings.ini
|
||||
speech_to_text_models/
|
||||
speech_to_text_models/*
|
||||
!speech_to_text_models/Where to get STT models.txt
|
||||
build/
|
||||
backend/
|
||||
language_detection_model/
|
||||
language_detection_model/*
|
||||
!language_detection_model/Where to get language detection model.txt
|
||||
.vscode/
|
||||
.vscode/settings.json
|
||||
package-lock.json
|
||||
src/sounds/tts/*
|
||||
loquendoBot_backend.spec
|
||||
forge.config.js
|
||||
backend/*
|
||||
src/backend/loquendoBot_backend.exe
|
||||
3
.vscode/settings.json
vendored
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"npm-scripts.showStartNotification": false
|
||||
}
|
||||
|
|
@ -2,17 +2,14 @@ module.exports = {
|
|||
packagerConfig: {
|
||||
icon: './src/images/icon.ico',
|
||||
asar: true,
|
||||
"extraResource": [
|
||||
"./src/config/loquendo.db",
|
||||
"./src/sounds"
|
||||
]
|
||||
extraResource: ['./src/config/loquendo.db', './src/sounds', './backend', './language_detection_model', './speech_to_text_models'],
|
||||
},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
config: {
|
||||
setupIcon: './src/images/icon.ico'
|
||||
setupIcon: './src/images/icon.ico',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -22,14 +19,12 @@ module.exports = {
|
|||
{
|
||||
name: '@electron-forge/maker-deb',
|
||||
config: {
|
||||
options: {
|
||||
}
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-rpm',
|
||||
config: {
|
||||
},
|
||||
config: {},
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
https://fasttext.cc/docs/en/language-identification.html
|
||||
7100
package-lock.json
generated
15
package.json
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"name": "loquendo-bot",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"description": "Bot assistant for streamers over different platforms",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"package": "npm run backend && electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "echo \"No linting configured\""
|
||||
"backend": "pyinstaller --noconsole --onefile --collect-all vosk --distpath ./backend ./src/backend/loquendoBot_backend.py"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
|
|
@ -20,18 +20,11 @@
|
|||
"axios": "^1.4.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"express": "^4.18.2",
|
||||
"franc": "^6.1.0",
|
||||
"i18next-electron-language-detector": "^0.0.10",
|
||||
"ini": "^2.0.0",
|
||||
"kill-port": "^2.0.1",
|
||||
"langdetect": "^0.2.1",
|
||||
"node-google-tts-api": "^1.1.1",
|
||||
"querystring": "^0.2.1",
|
||||
"request": "^2.88.2",
|
||||
"say": "^0.16.0",
|
||||
"socket.io": "^4.7.1",
|
||||
"socket.io-client": "^4.7.1",
|
||||
"sound-play": "^1.1.0",
|
||||
"tmi.js": "^1.8.5",
|
||||
"url": "^0.11.1",
|
||||
"winston": "^3.10.0",
|
||||
|
|
@ -44,6 +37,6 @@
|
|||
"@electron-forge/maker-squirrel": "^6.2.1",
|
||||
"@electron-forge/maker-zip": "^6.2.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^6.2.1",
|
||||
"electron": "25.4.0"
|
||||
"electron": "^25.9.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
speech_to_text_models/Where to get STT models.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
https://alphacephei.com/vosk/models
|
||||
279
src/backend/loquendoBot_backend.py
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
from flask import Flask, Response, jsonify, request
|
||||
import gevent
|
||||
import gevent.monkey
|
||||
import json
|
||||
|
||||
gevent.monkey.patch_all()
|
||||
import gevent.queue
|
||||
|
||||
import configparser
|
||||
import pyttsx3
|
||||
import sys
|
||||
import os
|
||||
|
||||
import queue
|
||||
import sys
|
||||
import sounddevice as sd
|
||||
|
||||
import fasttext
|
||||
|
||||
from deep_translator import (
|
||||
MyMemoryTranslator,
|
||||
)
|
||||
|
||||
import emoji
|
||||
|
||||
from vosk import Model, KaldiRecognizer, SetLogLevel
|
||||
|
||||
# global variables
|
||||
|
||||
SetLogLevel(-1)
|
||||
|
||||
settings = configparser.ConfigParser()
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
settingsPath = os.path.normpath(sys.argv[1])
|
||||
environment = sys.argv[2]
|
||||
q = queue.Queue()
|
||||
|
||||
|
||||
# gobal functions
|
||||
|
||||
# classes
|
||||
|
||||
|
||||
class LanguageDetection:
|
||||
def __init__(self):
|
||||
if environment == "dev":
|
||||
settings_folder = os.path.dirname(settingsPath)
|
||||
src_folder = os.path.dirname(settings_folder)
|
||||
main_folder = os.path.dirname(src_folder)
|
||||
language_detection_model = os.path.join(
|
||||
main_folder, "language_detection_model", f"lid.176.bin"
|
||||
)
|
||||
else:
|
||||
resources_folder = os.path.dirname(settingsPath)
|
||||
language_detection_model = os.path.join(
|
||||
resources_folder, "language_detection_model", f"lid.176.bin"
|
||||
)
|
||||
|
||||
language_detection_model = (
|
||||
rf"{language_detection_model}"
|
||||
)
|
||||
self.model = fasttext.load_model(language_detection_model)
|
||||
|
||||
def predict_lang(self, text):
|
||||
predictions = self.model.predict(text, k=5) # returns top 2 matching languages
|
||||
language_codes = []
|
||||
for prediction in predictions[0]:
|
||||
language_codes.append(prediction.replace("__label__", ""))
|
||||
|
||||
return language_codes
|
||||
|
||||
class STT:
|
||||
samplerate = None
|
||||
args = ""
|
||||
remaining = ""
|
||||
|
||||
def __init__(self):
|
||||
settings.read(settingsPath)
|
||||
device_info = sd.query_devices(int(settings["STT"]["MICROPHONE"]), "input")
|
||||
self.samplerate = int(device_info["default_samplerate"])
|
||||
|
||||
if environment == "dev":
|
||||
settings_folder = os.path.dirname(settingsPath)
|
||||
src_folder = os.path.dirname(settings_folder)
|
||||
main_folder = os.path.dirname(src_folder)
|
||||
vosk_model = os.path.join(
|
||||
main_folder, "speech_to_text_models", settings["STT"]["LANGUAGE"]
|
||||
)
|
||||
else:
|
||||
resources_folder = os.path.dirname(settingsPath)
|
||||
vosk_model = os.path.join(
|
||||
resources_folder, "speech_to_text_models", settings["STT"]["LANGUAGE"]
|
||||
)
|
||||
|
||||
self.model = Model(
|
||||
rf"{vosk_model}"
|
||||
)
|
||||
self.dump_fn = None
|
||||
|
||||
self.q = gevent.queue.Queue()
|
||||
self.rec = None
|
||||
self.is_running = False
|
||||
|
||||
def callback(self, indata, frames, time, status):
|
||||
if status:
|
||||
print(status, file=sys.stderr)
|
||||
self.q.put(bytes(indata))
|
||||
|
||||
def start_recognition(self):
|
||||
self.is_running = True
|
||||
|
||||
with sd.RawInputStream(
|
||||
samplerate=self.samplerate,
|
||||
blocksize=8000,
|
||||
device=0, # Default microphone
|
||||
dtype="int16",
|
||||
channels=1,
|
||||
callback=self.callback,
|
||||
):
|
||||
self.rec = KaldiRecognizer(self.model, self.samplerate)
|
||||
while True:
|
||||
data = self.q.get()
|
||||
if self.rec.AcceptWaveform(data):
|
||||
result = self.rec.Result()
|
||||
result_json = json.loads(str(result))
|
||||
yield f"data: {result_json}\n\n"
|
||||
else:
|
||||
partialResult = self.rec.PartialResult()
|
||||
result_json = json.loads(str(partialResult))
|
||||
yield f"data: {result_json}\n\n"
|
||||
|
||||
def stop_recognition(self):
|
||||
self.is_running = False
|
||||
|
||||
|
||||
speech_recognition_service = STT()
|
||||
|
||||
|
||||
class TTS:
|
||||
engine = None
|
||||
rate = None
|
||||
|
||||
def __init__(self):
|
||||
self.engine = pyttsx3.init()
|
||||
|
||||
def say(self, message, voice, count):
|
||||
voices = self.engine.getProperty("voices")
|
||||
for item in voices:
|
||||
if item.name == voice:
|
||||
matching_id = item.id
|
||||
break
|
||||
self.engine.setProperty("voice", matching_id)
|
||||
|
||||
if environment == "dev":
|
||||
settings_folder = os.path.dirname(settingsPath)
|
||||
src_folder = os.path.dirname(settings_folder)
|
||||
saveLocation = os.path.join(
|
||||
src_folder, "sounds\\tts", f"Internal_{count}.mp3"
|
||||
)
|
||||
else:
|
||||
resources_folder = os.path.dirname(settingsPath)
|
||||
saveLocation = os.path.join(
|
||||
resources_folder, "sounds\\tts", f"Internal_{count}.mp3"
|
||||
)
|
||||
|
||||
self.engine.save_to_file(message, saveLocation)
|
||||
self.engine.runAndWait()
|
||||
|
||||
def voices(self):
|
||||
voices = self.engine.getProperty("voices")
|
||||
self.engine.say(
|
||||
""
|
||||
) # engine breaks if you do not say something after getting voices
|
||||
self.engine.runAndWait()
|
||||
|
||||
return [voice.name for voice in voices]
|
||||
|
||||
|
||||
text_to_speech_service = TTS()
|
||||
|
||||
# endpoints
|
||||
|
||||
|
||||
@app.route("/stream", methods=["GET"])
|
||||
def stream_recognition():
|
||||
def generate():
|
||||
return speech_recognition_service.start_recognition()
|
||||
|
||||
return Response(generate(), content_type="text/event-stream")
|
||||
|
||||
|
||||
@app.route("/stop", methods=["POST"])
|
||||
def stop_recording():
|
||||
speech_recognition_service.stop_recognition()
|
||||
return Response("Speech recognition stopped", status=200)
|
||||
|
||||
|
||||
# @app.before_request
|
||||
# def custom_warning():
|
||||
# if environment == "dev":
|
||||
# print(
|
||||
# # "Running in internal development environment. This server is not for production use."
|
||||
# )
|
||||
|
||||
|
||||
@app.route("/terminate", methods=["GET"])
|
||||
def terminate_processes():
|
||||
shutdown_server()
|
||||
os._exit(0)
|
||||
|
||||
|
||||
def shutdown_server():
|
||||
func = request.environ.get("sever shutdown")
|
||||
if func is None:
|
||||
raise RuntimeError("Server is not running")
|
||||
func()
|
||||
|
||||
|
||||
# @app.route("/detect", methods=["POST"])
|
||||
# def server_status():
|
||||
# try:
|
||||
# request_data = request.json
|
||||
# message = request_data.get("message", "")
|
||||
# confidence_values = detector.compute_language_confidence_values(message)
|
||||
# for language, value in confidence_values:
|
||||
# print(f"{language.name}: {value:.2f}")
|
||||
# message = request_data.get("message", "")
|
||||
# except Exception as e:
|
||||
# return jsonify({"error": "An error occurred"}), 500
|
||||
# return jsonify({"message": "Audio triggered"}), 200
|
||||
|
||||
|
||||
@app.route("/status", methods=["GET"])
|
||||
def server_status():
|
||||
return jsonify({"status": "server is running"})
|
||||
|
||||
|
||||
@app.route("/audio", methods=["POST"])
|
||||
def trigger_backend_event():
|
||||
try:
|
||||
request_data = request.json
|
||||
message = request_data.get("message", "")
|
||||
voice = request_data.get("voice")
|
||||
count = request_data.get("count")
|
||||
text_to_speech_service.say(message, voice, count)
|
||||
except Exception as e:
|
||||
return jsonify({"error": "An error occurred"}), 500
|
||||
return jsonify({"message": "Audio triggered"}), 200
|
||||
|
||||
|
||||
@app.route("/voices", methods=["GET"])
|
||||
def get_voices():
|
||||
try:
|
||||
voices = text_to_speech_service.voices()
|
||||
return jsonify({"voices": voices}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": "An error occurred"}), 500
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
LANGUAGE = LanguageDetection()
|
||||
lang = LANGUAGE.predict_lang("hola cómo estás")
|
||||
print(lang)
|
||||
text = "Keep it up. You are awesome"
|
||||
translated = MyMemoryTranslator(
|
||||
source="english", target="spanish latin america"
|
||||
).translate(text)
|
||||
print(translated)
|
||||
if len(sys.argv) > 1:
|
||||
settings.read(settingsPath)
|
||||
port = int(settings["GENERAL"]["PORT"])
|
||||
else:
|
||||
environment = "dev"
|
||||
port = 9000
|
||||
stream_recognition()
|
||||
|
||||
app.run(host="127.0.0.1", port=port)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
EN
|
||||
ES
|
||||
NL
|
||||
163
src/css/chat.css
|
|
@ -1,6 +1,5 @@
|
|||
@font-face {
|
||||
font-family: 'FRAMDCN';
|
||||
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
@ -45,7 +44,7 @@ h1 {
|
|||
border-bottom-right-radius: 2px;
|
||||
max-width: 80%;
|
||||
margin-right: 10px;
|
||||
animation: floatup .5s forwards;
|
||||
animation: floatup 0.5s forwards;
|
||||
}
|
||||
|
||||
.botText {
|
||||
|
|
@ -65,13 +64,13 @@ h1 {
|
|||
border-bottom-left-radius: 2px;
|
||||
max-width: 80%;
|
||||
margin-left: 10px;
|
||||
animation: floatup .5s forwards
|
||||
animation: floatup 0.5s forwards;
|
||||
}
|
||||
|
||||
@keyframes floatup {
|
||||
from {
|
||||
transform: translateY(14px);
|
||||
opacity: .0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
|
|
@ -80,7 +79,7 @@ h1 {
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width:600px) {
|
||||
@media screen and (max-width: 600px) {
|
||||
.full-chat-block {
|
||||
width: 100%;
|
||||
border-radius: 0px;
|
||||
|
|
@ -159,44 +158,38 @@ h1 {
|
|||
margin: 10px;
|
||||
}
|
||||
|
||||
.chat-input input[good]+button {
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .12), 0 2px 4px rgba(0, 0, 0, .24);
|
||||
.chat-input input[good] + button {
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.chat-input input[good]+button:hover {
|
||||
.chat-input input[good] + button:hover {
|
||||
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
/* filter: brightness(150%); */
|
||||
}
|
||||
|
||||
.chat-input input[good]+button path {
|
||||
.chat-input input[good] + button path {
|
||||
fill: var(--chat-bubble-message);
|
||||
}
|
||||
|
||||
.msg-container {
|
||||
position: relative;
|
||||
position: static;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 0px 0px 10px 0px;
|
||||
}
|
||||
|
||||
.msg-box {
|
||||
display: flex;
|
||||
background: var(--chat-bubble);
|
||||
padding: 5px 5px 5px 5px;
|
||||
border-radius: 6px 6px 6px 6px;
|
||||
margin-left: -20px;
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
max-width: 80%;
|
||||
width: auto;
|
||||
float: left;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .12), 0 2px 4px rgba(0, 0, 0, .24);
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
padding: 20px 5px 5px 25px;
|
||||
margin: 20px 0px 0px 25px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24);
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.msg-box-user {
|
||||
display: flex;
|
||||
background: var(--chat-bubble);
|
||||
padding: 5px 5px 5px 5px;
|
||||
border-radius: 6px 6px 6px 6px;
|
||||
|
|
@ -206,7 +199,7 @@ h1 {
|
|||
width: auto;
|
||||
float: right;
|
||||
word-wrap: break-word;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .12), 0 2px 4px rgba(0, 0, 0, .24);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.msg-box-user-temp {
|
||||
|
|
@ -216,9 +209,8 @@ h1 {
|
|||
.user-img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin: 0 10px 10px 0;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.user-img-user {
|
||||
|
|
@ -229,45 +221,33 @@ h1 {
|
|||
margin: 0 0px 10px 10px;
|
||||
}
|
||||
|
||||
.flr {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.messages {
|
||||
margin-left: 20px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.messages-user {
|
||||
margin-right: 20px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.msg {
|
||||
font-size: 11pt;
|
||||
line-height: 13pt;
|
||||
font-size: 12pt;
|
||||
color: var(--chat-bubble-message);
|
||||
margin: 0 0 4px 0;
|
||||
display: flex;
|
||||
align-items: self-end;
|
||||
margin: 0 0 0 0;
|
||||
}
|
||||
|
||||
.msg-temp {
|
||||
color: var(--chat-bubble-message-temp);
|
||||
}
|
||||
|
||||
.msg:first-of-type {
|
||||
/* .msg:first-of-type {
|
||||
margin-top: 8px;
|
||||
}
|
||||
} */
|
||||
|
||||
.timestamp {
|
||||
color: var(--chat-bubble-header);
|
||||
font-size: 10pt;
|
||||
align-items: center;
|
||||
font-family: "xxii_avenmedium";
|
||||
font-family: 'xxii_avenmedium';
|
||||
}
|
||||
|
||||
.timestamp-temp {
|
||||
|
|
@ -277,7 +257,14 @@ h1 {
|
|||
.username {
|
||||
float: left;
|
||||
color: var(--chat-bubble-header);
|
||||
font-weight: bold;
|
||||
background-color: var(--main-color4);
|
||||
margin-left: 25px;
|
||||
color: white;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 5px 5px 5px 30px;
|
||||
border-radius: 5px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.username-temp {
|
||||
|
|
@ -286,7 +273,9 @@ h1 {
|
|||
|
||||
.post-time {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
padding: 10px 0px 0px 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.msg-self .msg-box {
|
||||
|
|
@ -311,10 +300,11 @@ h1 {
|
|||
.icon-container {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
position: absolute;
|
||||
float: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.icon-container-user {
|
||||
|
|
@ -324,6 +314,7 @@ h1 {
|
|||
float: right;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.img {
|
||||
|
|
@ -336,10 +327,8 @@ h1 {
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin-left: -20px;
|
||||
margin-top: 10px;
|
||||
margin-left: -15px;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.status-circle-user {
|
||||
|
|
@ -353,19 +342,55 @@ h1 {
|
|||
}
|
||||
|
||||
select {
|
||||
}
|
||||
|
||||
.menu-select {
|
||||
font-size: 0.9rem;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--main-color3);
|
||||
color: var(--main-color2);
|
||||
align-items: center;
|
||||
border: 0px;
|
||||
padding-left: 10px;
|
||||
width: 300px;
|
||||
font-size: 100%;
|
||||
padding: 10px;
|
||||
padding-right: 40px;
|
||||
padding-right: 25px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='34' viewBox='0 0 24 24' width='32' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
background-position-x: 95%;
|
||||
background-position-x: 100%;
|
||||
background-position-y: 5px;
|
||||
}
|
||||
|
||||
.top-select {
|
||||
width: auto;
|
||||
height: 24px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.info-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.top-select option {
|
||||
margin: 40px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
|
||||
background-color: var(--top-bar);
|
||||
}
|
||||
|
||||
.AdvancedMenu {
|
||||
border: 1px var(--main-color2) solid;
|
||||
margin-top: 10px;
|
||||
|
|
@ -394,7 +419,7 @@ select {
|
|||
font-size: 10pt;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
width: 125px
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
.AdvancedMenuLabel2 {
|
||||
|
|
@ -403,6 +428,12 @@ select {
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.AdvancedMenuLabel3 {
|
||||
font-size: 12pt;
|
||||
padding-right: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#SaveAdvancedSettingsButton {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
|
@ -416,7 +447,6 @@ select {
|
|||
border-radius: 20px;
|
||||
}
|
||||
|
||||
|
||||
/* After slide changes */
|
||||
|
||||
.toggle:after {
|
||||
|
|
@ -430,31 +460,26 @@ select {
|
|||
top: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* Checkbox checked effect */
|
||||
|
||||
.checkbox:checked+.toggle::after {
|
||||
.checkbox:checked + .toggle::after {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
|
||||
/* Checkbox checked toggle label bg color */
|
||||
|
||||
.checkbox:checked+.toggle {
|
||||
.checkbox:checked + .toggle {
|
||||
background-color: var(--main-color1);
|
||||
}
|
||||
|
||||
|
||||
/* Checkbox vanished */
|
||||
|
||||
.checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Small toggle */
|
||||
|
||||
|
||||
/* toggle in label designing */
|
||||
|
||||
.toggle-small {
|
||||
|
|
@ -464,9 +489,9 @@ select {
|
|||
height: 20px;
|
||||
background-color: var(--main-color3);
|
||||
border-radius: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* After slide changes */
|
||||
|
||||
.toggle-small:after {
|
||||
|
|
@ -480,16 +505,14 @@ select {
|
|||
top: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* Checkbox checked effect */
|
||||
|
||||
.checkbox:checked+.toggle-small::after {
|
||||
.checkbox:checked + .toggle-small::after {
|
||||
left: 13px;
|
||||
}
|
||||
|
||||
|
||||
/* Checkbox checked toggle label bg color */
|
||||
|
||||
.checkbox:checked+.toggle-small {
|
||||
.checkbox:checked + .toggle-small {
|
||||
background-color: var(--main-color1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,28 @@
|
|||
/* Basic styling */
|
||||
|
||||
:root {
|
||||
overflow: hidden;
|
||||
--main-color1: #6e2c8c;
|
||||
--main-color1-temp: #6e2c8c;
|
||||
/*Left bar and top right bar*/
|
||||
--main-color2: white;
|
||||
--main-color2-temp: white;
|
||||
/*Icons and text*/
|
||||
--main-color3: #211E1E;
|
||||
--main-color3-temp: #211E1E;
|
||||
--main-color3: #211e1e;
|
||||
--main-color3-temp: #211e1e;
|
||||
/*Buttons and input*/
|
||||
--main-color4: #2f2c34;
|
||||
--main-color4-temp: #2f2c34;
|
||||
--top-bar: #100B12;
|
||||
--top-bar-temp: #100B12;
|
||||
--top-bar: #100b12;
|
||||
--top-bar-temp: #100b12;
|
||||
--mid-section: #352d3d;
|
||||
--mid-section-temp: #352d3d;
|
||||
--chat-bubble: #7A6D7F;
|
||||
--chat-bubble-temp: #7A6D7F;
|
||||
--chat-bubble: #7a6d7f;
|
||||
--chat-bubble-header: #141414;
|
||||
--chat-bubble-header-temp: #141414;
|
||||
--chat-bubble-username: white;
|
||||
--chat-bubble-message: white;
|
||||
--chat-bubble-temp: #7a6d7f;
|
||||
--chat-bubble-header-temp: #141414;
|
||||
--chat-bubble-message-temp: white;
|
||||
}
|
||||
|
||||
|
|
@ -44,11 +46,10 @@ body {
|
|||
}
|
||||
|
||||
body {
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* Styling of window frame and titlebar */
|
||||
|
||||
body {
|
||||
|
|
@ -76,6 +77,7 @@ body {
|
|||
margin-top: 32px;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
|
|
@ -86,24 +88,20 @@ body {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-app-region: drag;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
color: var(--main-color2);
|
||||
}
|
||||
|
||||
#titlebar #drag-region {
|
||||
display: grid;
|
||||
grid-template-columns: auto 138px;
|
||||
}
|
||||
|
||||
#window-title {
|
||||
grid-column: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
overflow: hidden;
|
||||
font-family: "Segoe UI", sans-serif;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
|
@ -125,9 +123,6 @@ body {
|
|||
top: 0;
|
||||
right: 0;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
#window-controls {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
|
|
@ -141,11 +136,11 @@ body {
|
|||
}
|
||||
|
||||
@media (-webkit-device-pixel-ratio: 1.5),
|
||||
(device-pixel-ratio: 1.5),
|
||||
(-webkit-device-pixel-ratio: 2),
|
||||
(device-pixel-ratio: 2),
|
||||
(-webkit-device-pixel-ratio: 3),
|
||||
(device-pixel-ratio: 3) {
|
||||
(device-pixel-ratio: 1.5),
|
||||
(-webkit-device-pixel-ratio: 2),
|
||||
(device-pixel-ratio: 2),
|
||||
(-webkit-device-pixel-ratio: 3),
|
||||
(device-pixel-ratio: 3) {
|
||||
#window-controls .icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
|
@ -168,16 +163,16 @@ body {
|
|||
#close-button:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
/* border-top-right-radius: 20px; */
|
||||
background: #F1707A !important;
|
||||
background: #f1707a !important;
|
||||
}
|
||||
|
||||
#close-button:active {
|
||||
background: #F1707A !important;
|
||||
background: #f1707a !important;
|
||||
}
|
||||
|
||||
#close-button:active .icon {
|
||||
filter: invert(1);
|
||||
background: #F1707A !important;
|
||||
background: #f1707a !important;
|
||||
}
|
||||
|
||||
#min-button {
|
||||
|
|
@ -205,15 +200,29 @@ body {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
.active-mic {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.about {
|
||||
-webkit-app-region: no-drag;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 32px;
|
||||
text-align: -webkit-center;
|
||||
}
|
||||
|
||||
.language-selector {
|
||||
position: absolute;
|
||||
-webkit-app-region: no-drag;
|
||||
display: inline-block;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.language-dropdown {
|
||||
|
|
@ -223,9 +232,8 @@ body {
|
|||
width: 55px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 2;
|
||||
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
font-family: 'NotoColorEmojiLimited', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
}
|
||||
|
||||
.language-item {
|
||||
|
|
@ -242,4 +250,4 @@ body {
|
|||
font-family: NotoColorEmojiLimited;
|
||||
unicode-range: U+1F1E6-1F1FF;
|
||||
src: url(https://raw.githack.com/googlefonts/noto-emoji/main/fonts/NotoColorEmoji.ttf);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
#rpe {
|
||||
font-size: 8pt;
|
||||
margin: 2px 0px 0px 0px
|
||||
margin: 2px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.menu .items .item {
|
||||
|
|
@ -78,7 +78,6 @@
|
|||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
|
||||
.sidepanel-left {
|
||||
position: relative;
|
||||
width: 50px;
|
||||
|
|
@ -87,7 +86,7 @@
|
|||
font-family: Helvetica;
|
||||
text-align: center;
|
||||
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
|
||||
transition: .3s ease-in-out;
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.sidepanel-right {
|
||||
|
|
@ -98,7 +97,7 @@
|
|||
font-family: Helvetica;
|
||||
text-align: center;
|
||||
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
|
||||
transition: .3s ease-in-out;
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.collapse-menu-left {
|
||||
|
|
@ -134,7 +133,7 @@
|
|||
cursor: pointer;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
transition: .3s ease-in-out;
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.collapse-circle-left {
|
||||
|
|
@ -156,7 +155,7 @@
|
|||
cursor: pointer;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
transition: .3s ease-in-out;
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.collapse-circle-right {
|
||||
|
|
@ -224,4 +223,4 @@
|
|||
flex: 2;
|
||||
background-color: var(--main-color4-temp);
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,19 +53,19 @@ input[type='range'].styled-slider::-webkit-slider-runnable-track {
|
|||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress1::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress2::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress3::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress4::-webkit-slider-runnable-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #1a1a1a;
|
||||
}
|
||||
|
||||
/*mozilla*/
|
||||
|
|
@ -82,24 +82,24 @@ input[type='range'].styled-slider::-moz-range-track {
|
|||
height: 40px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
background: #1a1a1a;
|
||||
background: var(--main-color3);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress1::-moz-range-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress2::-moz-range-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress3::-moz-range-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
}
|
||||
|
||||
input[type='range'].styled-slider.slider-progress4::-moz-range-track {
|
||||
background: linear-gradient(#7b2cbf, #7b2cbf) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
background: linear-gradient(var(--main-color1), var(--main-color1)) 0 / var(--sx) 100% no-repeat, #464646;
|
||||
}
|
||||
|
||||
/*ms*/
|
||||
|
|
@ -127,7 +127,7 @@ input[type='range'].styled-slider::-ms-thumb {
|
|||
input[type='range'].styled-slider::-ms-track {
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
background: #1a1a1a;
|
||||
background: var(--main-color3);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
|
|
@ -137,7 +137,7 @@ input[type='range'].styled-slider.slider-progress1::-ms-fill-lower {
|
|||
height: 40px;
|
||||
border-radius: 1em 0 0 1em;
|
||||
margin: -undefined 0 -undefined -undefined;
|
||||
background: #7b2cbf;
|
||||
background: var(--main-color1);
|
||||
border: none;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ input[type='range'].styled-slider.slider-progress2::-ms-fill-lower {
|
|||
height: 40px;
|
||||
border-radius: 1em 0 0 1em;
|
||||
margin: -undefined 0 -undefined -undefined;
|
||||
background: #7b2cbf;
|
||||
background: var(--main-color1);
|
||||
border: none;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ input[type='range'].styled-slider.slider-progress3::-ms-fill-lower {
|
|||
height: 40px;
|
||||
border-radius: 1em 0 0 1em;
|
||||
margin: -undefined 0 -undefined -undefined;
|
||||
background: #7b2cbf;
|
||||
background: var(--main-color1);
|
||||
border: none;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ input[type='range'].styled-slider.slider-progress4::-ms-fill-lower {
|
|||
height: 40px;
|
||||
border-radius: 1em 0 0 1em;
|
||||
margin: -undefined 0 -undefined -undefined;
|
||||
background: #7b2cbf;
|
||||
background: var(--main-color1);
|
||||
border: none;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,21 +191,9 @@ input:checked + label {
|
|||
height: 32 px;
|
||||
}
|
||||
|
||||
select {
|
||||
font-size: 0.9rem;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--main-color3);
|
||||
color: var(--main-color2);
|
||||
align-items: center;
|
||||
border: 0px;
|
||||
padding-left: 20px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.language {
|
||||
width: 80px;
|
||||
margin-left: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#AdvancedMenu_mask {
|
||||
|
|
@ -304,7 +292,7 @@ input[type='lol'] {
|
|||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
left: 425px;
|
||||
left: 450px;
|
||||
}
|
||||
|
||||
/* Hide the default appearance of the button */
|
||||
|
|
@ -315,19 +303,17 @@ input[type='lol'] {
|
|||
/* Style the reveal icon (you can use your preferred icon or font) */
|
||||
.password-toggle-icon {
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
color: var(--main-color2);
|
||||
}
|
||||
|
||||
#toasts {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
/* Adjust the distance from the bottom of the screen */
|
||||
right: 50%;
|
||||
right: 0%;
|
||||
/* Center the toasts horizontally */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* Center the toasts horizontally */
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
#tstx {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 40px;
|
||||
margin-left: 50px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.optionrow {
|
||||
|
|
@ -50,20 +49,16 @@
|
|||
font-size: 12pt;
|
||||
}
|
||||
|
||||
#TTSTest {
|
||||
width: 296px;
|
||||
height: 85px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size: 14pt;
|
||||
height: 60px;
|
||||
padding: 5px;
|
||||
width: 300px;
|
||||
resize: none;
|
||||
border-radius: 5px;
|
||||
background: var(--main-color3);
|
||||
color: var(--main-color2);
|
||||
font-family: 'xxii_avenmedium';
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.SaveConfig {
|
||||
|
|
@ -73,7 +68,6 @@ textarea {
|
|||
justify-content: center;
|
||||
color: var(--main-color2);
|
||||
margin-bottom: 10px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.SmallButton {
|
||||
|
|
@ -129,7 +123,39 @@ textarea {
|
|||
}
|
||||
|
||||
.AdvancedMenuIcon {
|
||||
/* filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%); */
|
||||
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(350deg) brightness(104%) contrast(101%);
|
||||
align-items: flex-start;
|
||||
margin: auto;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.AdvancedMenuIcon2 {
|
||||
align-items: flex-start;
|
||||
margin: auto;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
input:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
select:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
textarea:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
label:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
.circle-right:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
.circle-left:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
|
|
|||
BIN
src/images/amazon.png
Normal file
|
After Width: | Height: | Size: 787 B |
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24" height="24px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Amazon-color</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
|
||||
</defs>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Color-" transform="translate(-601.000000, -560.000000)">
|
||||
<g id="Amazon" transform="translate(601.000000, 560.000000)">
|
||||
<path d="M25.4026553,25.9595294 C24.660417,27.4418824 23.3876054,28.3962353 22.0103725,28.7181176 C21.8015298,28.7181176 21.4826213,28.8225882 21.1637129,28.8225882 C18.835399,28.8225882 17.458166,27.0211765 17.458166,24.3727059 C17.458166,20.9788235 19.4703937,19.392 22.0103725,18.6465882 C23.3876054,18.3303529 24.9793255,18.2230588 26.5682233,18.2230588 L26.5682233,19.4964706 C26.5682233,21.9331765 26.6726447,23.8390588 25.4026553,25.9595294 L25.4026553,25.9595294 Z M26.5682233,13.3524706 C25.1909904,13.4569412 23.5992703,13.5614118 22.0103725,13.7703529 C19.574815,14.0922353 17.1392576,14.5157647 15.1298521,15.4701176 C11.2098182,17.0597647 8.55977364,20.4508235 8.55977364,25.4287059 C8.55977364,31.6856471 12.5842289,34.8621176 17.6726531,34.8621176 C19.3659723,34.8621176 20.7432053,34.6475294 22.0103725,34.3341176 C24.0282445,33.696 25.7187415,32.5298824 27.7309692,30.4094118 C28.8965372,31.9990588 29.2182679,32.7444706 31.2276733,34.4385882 C31.7582467,34.6475294 32.28882,34.6475294 32.7093276,34.3341176 C33.9821392,33.2724706 36.208854,31.3637647 37.3715998,30.3049412 C37.9021732,29.8814118 37.7977518,29.2432941 37.4760212,28.7181176 C36.3132753,27.2329412 35.1448851,25.9595294 35.1448851,23.0992941 L35.1448851,13.5614118 C35.1448851,9.53505882 35.4666157,5.82494118 32.5004849,3.072 C30.0649275,0.849882353 26.2493149,0 23.2831841,0 L22.0103725,0 C16.6115064,0.313411765 10.8937319,2.64564706 9.61809814,9.32329412 C9.40643324,10.1731765 10.0442501,10.4894118 10.4675799,10.5938824 L16.3998415,11.3364706 C17.0348362,11.2291765 17.3537447,10.6983529 17.458166,10.1731765 C17.9859172,7.84094118 19.8937235,6.67482353 22.0103725,6.46023529 L22.4365245,6.46023529 C23.7093361,6.46023529 25.086569,6.99105882 25.8259851,8.05270588 C26.6726447,9.32329412 26.5682233,11.0202353 26.5682233,12.5054118 L26.5682233,13.3524706 L26.5682233,13.3524706 Z" fill="#343B45">
|
||||
|
||||
</path>
|
||||
<path d="M47.9943556,35.9463529 L47.9943556,35.9435294 C47.971778,35.4437647 47.8673567,35.0625882 47.658514,34.7463529 L47.6359364,34.7152941 L47.6105366,34.6842353 C47.3988717,34.4527059 47.1956734,34.3651765 46.9755419,34.2691765 C46.3179696,34.0150588 45.3612442,33.8795294 44.2097872,33.8767059 C43.382883,33.8767059 42.4713128,33.9557647 41.5540982,34.1562353 L41.551276,34.0941176 L40.6284171,34.4018824 L40.6114839,34.4103529 L40.0893771,34.5797647 L40.0893771,34.6023529 C39.47696,34.8564706 38.9209869,35.1727059 38.4045245,35.5482353 C38.0827939,35.7882353 37.8175072,36.1072941 37.8033962,36.5957647 C37.7949296,36.8611765 37.9303952,37.1661176 38.1533489,37.3468235 C38.3763025,37.5275294 38.6359448,37.5896471 38.8645429,37.5896471 C38.9181647,37.5896471 38.9689643,37.5868235 39.0141194,37.5783529 L39.0592746,37.5755294 L39.093141,37.5698824 C39.5446928,37.4738824 40.2022651,37.4089412 40.9727253,37.3016471 C41.6331198,37.2282353 42.3330251,37.1745882 42.9397978,37.1745882 C43.368772,37.1717647 43.7554132,37.2028235 44.0206999,37.2592941 C44.1533432,37.2875294 44.2521202,37.3214118 44.3057419,37.3496471 C44.3254973,37.3552941 44.3396083,37.3637647 44.3480749,37.3694118 C44.3593637,37.4061176 44.3762969,37.5021176 44.3734747,37.6348235 C44.3791191,38.1430588 44.164632,39.0861176 43.8683012,40.0065882 C43.5804369,40.9270588 43.2304843,41.8503529 42.999064,42.4630588 C42.94262,42.6042353 42.9059314,42.7595294 42.9059314,42.9289412 C42.900287,43.1745882 43.0018862,43.4738824 43.2163733,43.6715294 C43.425216,43.8691765 43.696147,43.9482353 43.9219229,43.9482353 L43.9332117,43.9482353 C44.2718756,43.9454118 44.5597398,43.8098824 44.8080933,43.6150588 C47.1505182,41.5087059 47.9661336,38.1430588 48,36.2484706 L47.9943556,35.9463529 Z M41.0489247,38.8658824 C40.8090378,38.8630588 40.5635065,38.9195294 40.3349084,39.0268235 C40.0780883,39.1284706 39.8156239,39.2470588 39.5672704,39.3515294 L39.2032068,39.504 L38.7290774,39.6931765 L38.7290774,39.6988235 C33.5785648,41.7882353 28.16841,43.0136471 23.1618295,43.1209412 C22.9783866,43.1265882 22.7921215,43.1265882 22.614323,43.1265882 C14.7403887,43.1322353 8.31706456,39.4785882 1.83729642,35.8785882 C1.61152053,35.76 1.37727804,35.6978824 1.15150215,35.6978824 C0.860815683,35.6978824 0.561662624,35.808 0.344353327,36.0112941 C0.12704403,36.2174118 -0.00277710907,36.5138824 4.50895989e-05,36.816 C-0.00277710907,37.2084706 0.208887791,37.5698824 0.505218651,37.8042353 C6.58705678,43.0870588 13.25309,47.9943529 22.2192152,48 C22.3941915,48 22.57199,47.9943529 22.7497885,47.9915294 C28.453452,47.8644706 34.902176,45.936 39.9087564,42.7905882 L39.9398006,42.7708235 C40.5945507,42.3783529 41.2493008,41.9322353 41.8673623,41.4381176 C42.2511813,41.1529412 42.516468,40.7068235 42.516468,40.2437647 C42.4995348,39.4221176 41.8024517,38.8658824 41.0489247,38.8658824 Z" id="Fill-237" fill="#FF9A00">
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
BIN
src/images/google.png
Normal file
|
After Width: | Height: | Size: 571 B |
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24px" height="24px" viewBox="-0.5 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Google-color</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
|
||||
</defs>
|
||||
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Color-" transform="translate(-401.000000, -860.000000)">
|
||||
<g id="Google" transform="translate(401.000000, 860.000000)">
|
||||
<path d="M9.82727273,24 C9.82727273,22.4757333 10.0804318,21.0144 10.5322727,19.6437333 L2.62345455,13.6042667 C1.08206818,16.7338667 0.213636364,20.2602667 0.213636364,24 C0.213636364,27.7365333 1.081,31.2608 2.62025,34.3882667 L10.5247955,28.3370667 C10.0772273,26.9728 9.82727273,25.5168 9.82727273,24" id="Fill-1" fill="#FBBC05">
|
||||
|
||||
</path>
|
||||
<path d="M23.7136364,10.1333333 C27.025,10.1333333 30.0159091,11.3066667 32.3659091,13.2266667 L39.2022727,6.4 C35.0363636,2.77333333 29.6954545,0.533333333 23.7136364,0.533333333 C14.4268636,0.533333333 6.44540909,5.84426667 2.62345455,13.6042667 L10.5322727,19.6437333 C12.3545909,14.112 17.5491591,10.1333333 23.7136364,10.1333333" id="Fill-2" fill="#EB4335">
|
||||
|
||||
</path>
|
||||
<path d="M23.7136364,37.8666667 C17.5491591,37.8666667 12.3545909,33.888 10.5322727,28.3562667 L2.62345455,34.3946667 C6.44540909,42.1557333 14.4268636,47.4666667 23.7136364,47.4666667 C29.4455,47.4666667 34.9177955,45.4314667 39.0249545,41.6181333 L31.5177727,35.8144 C29.3995682,37.1488 26.7323182,37.8666667 23.7136364,37.8666667" id="Fill-3" fill="#34A853">
|
||||
|
||||
</path>
|
||||
<path d="M46.1454545,24 C46.1454545,22.6133333 45.9318182,21.12 45.6113636,19.7333333 L23.7136364,19.7333333 L23.7136364,28.8 L36.3181818,28.8 C35.6879545,31.8912 33.9724545,34.2677333 31.5177727,35.8144 L39.0249545,41.6181333 C43.3393409,37.6138667 46.1454545,31.6490667 46.1454545,24" id="Fill-4" fill="#4285F4">
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
src/images/icon-256.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
src/images/icon-512.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24px" height="24px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M703.90625 865.90625l-39.9375-15.65625 148.6875-380.0625c6.84375-17.53125-2.4375-37.3125-20.34375-43.125l-260.0625-84.9375c-16.6875-5.4375-34.6875 3.1875-40.875 19.59375L361.8125 704.46875l-61.40625-23.15625 181.875-481.125c6.09375-16.21875 23.8125-24.84375 40.40625-19.78125l389.0625 120.1875c18.1875 5.625 27.84375 25.5 20.90625 43.3125l-199.125 509.0625c-4.6875 11.71875-17.90625 17.53125-29.625 12.9375z" fill="#360682" /><path d="M241.0625 658.15625m-129.375 0a129.375 129.375 0 1 0 258.75 0 129.375 129.375 0 1 0-258.75 0Z" fill="#360682" /><path d="M614.375 802.90625m-129.375 0a129.375 129.375 0 1 0 258.75 0 129.375 129.375 0 1 0-258.75 0Z" fill="#360682" /><path d="M246.53333333 379.36666667L142.43333333 118.66666667c-6.8-16.9 2.4-36 19.8-41.3l107.9-32.9c14.4-4.4 29.7 2.5 36 16.2l13.2 28.8c7.5 16.3-0.9 35.4-17.9 41l-73.7 24.3c-6.9 2.3-10.5 9.9-7.8 16.6L290.33333333 350.96666667c2.1 5.4-0.5 11.4-5.9 13.6l-37.9 14.8z" fill="#E51C5A" /><path d="M211.53333333 379.36666667m-84.8 0a84.8 84.8 0 1 0 169.6 0 84.8 84.8 0 1 0-169.6 0Z" fill="#E51C5A" /></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
src/images/server.png
Normal file
|
After Width: | Height: | Size: 327 B |
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="24px" height="24px" viewBox="0 0 491.52 491.52" xml:space="preserve">
|
||||
<path style="fill:#64798A;" d="M400.903,0H90.615c-7.296,0-13.209,5.914-13.209,13.209v465.102c0,7.294,5.913,13.209,13.209,13.209
|
||||
h310.288c7.296,0,13.21-5.914,13.21-13.209V13.209C414.114,5.914,408.199,0,400.903,0z"/>
|
||||
<path style="fill:#3A556A;" d="M141.749,283.578h208.024c12.942,0,23.467-10.525,23.467-23.467V0.001h-8.936v260.11
|
||||
c0,8.012-6.519,14.53-14.531,14.53H141.749c-8.012,0-14.531-6.518-14.531-14.53V0.001h-8.936v260.11
|
||||
C118.281,273.052,128.806,283.578,141.749,283.578z"/>
|
||||
<circle style="fill:#EBF0F3;" cx="241.971" cy="341.146" r="21.852"/>
|
||||
<circle style="fill:#44C4A1;" cx="241.971" cy="380.16" r="7.752"/>
|
||||
<rect x="140.749" y="28.16" style="fill:#2F4859;" width="210.074" height="71.439"/>
|
||||
<rect x="145.193" y="32.645" style="fill:#3A556A;" width="201.114" height="62.505"/>
|
||||
<rect x="140.718" y="135.071" style="fill:#2F4859;" width="210.074" height="71.434"/>
|
||||
<rect x="145.193" y="139.571" style="fill:#3A556A;" width="201.114" height="62.505"/>
|
||||
<path style="fill:#D5D6DB;" d="M335.55,110.819h-32.736c-1.093,0-1.978,0.886-1.978,1.981v10.529c0,1.094,0.886,1.981,1.978,1.981
|
||||
h32.736c1.093,0,1.979-0.888,1.979-1.981V112.8C337.529,111.706,336.643,110.819,335.55,110.819z"/>
|
||||
<path style="fill:#EBF0F3;" d="M335.55,106.857h-32.736c-1.093,0-1.978,0.888-1.978,1.981v10.529c0,1.094,0.886,1.981,1.978,1.981
|
||||
h32.736c1.093,0,1.979-0.887,1.979-1.981v-10.529C337.529,107.745,336.643,106.857,335.55,106.857z"/>
|
||||
<path style="fill:#D5D6DB;" d="M335.55,218.06h-32.736c-1.093,0-1.978,0.886-1.978,1.981v10.529c0,1.094,0.886,1.981,1.978,1.981
|
||||
h32.736c1.093,0,1.979-0.887,1.979-1.981v-10.529C337.529,218.946,336.643,218.06,335.55,218.06z"/>
|
||||
<path style="fill:#EBF0F3;" d="M335.55,214.098h-32.736c-1.093,0-1.978,0.887-1.978,1.981v10.529c0,1.095,0.886,1.981,1.978,1.981
|
||||
h32.736c1.093,0,1.979-0.886,1.979-1.981v-10.529C337.529,214.985,336.643,214.098,335.55,214.098z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
src/images/settings.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
src/images/sound.png
Normal file
|
After Width: | Height: | Size: 619 B |
BIN
src/images/stt.png
Normal file
|
After Width: | Height: | Size: 542 B |
|
|
@ -1,48 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<defs>
|
||||
|
||||
<style>.cls-1{fill:#4285f4;}.cls-2{fill:none;}.cls-3{fill:#669df6;}</style>
|
||||
|
||||
</defs>
|
||||
|
||||
<title>Icon_24px_SpeechtoText_Color</title>
|
||||
|
||||
<g data-name="Product Icons">
|
||||
|
||||
<g data-name="colored-32/speech">
|
||||
|
||||
<rect class="cls-1" x="11" y="2" width="2" height="20"/>
|
||||
|
||||
<rect class="cls-2" width="24" height="24"/>
|
||||
|
||||
<g >
|
||||
|
||||
<rect id="Rectangle-path-2" data-name="Rectangle-path" class="cls-1" x="7" y="8" width="2" height="8"/>
|
||||
|
||||
<rect id="Rectangle-path-3" data-name="Rectangle-path" class="cls-1" x="15" y="8" width="2" height="8"/>
|
||||
|
||||
<rect id="Rectangle-path-4" data-name="Rectangle-path" class="cls-1" x="3" y="5" width="2" height="14"/>
|
||||
|
||||
<rect id="Rectangle-path-5" data-name="Rectangle-path" class="cls-1" x="19" y="5" width="2" height="14"/>
|
||||
|
||||
<rect id="Rectangle-path-6" data-name="Rectangle-path" class="cls-3" x="11" y="2" width="2" height="10"/>
|
||||
|
||||
<rect id="Rectangle-path-7" data-name="Rectangle-path" class="cls-3" x="7" y="8" width="2" height="4"/>
|
||||
|
||||
<rect id="Rectangle-path-8" data-name="Rectangle-path" class="cls-3" x="15" y="8" width="2" height="4"/>
|
||||
|
||||
<rect id="Rectangle-path-9" data-name="Rectangle-path" class="cls-3" x="3" y="5" width="2" height="7"/>
|
||||
|
||||
<rect id="Rectangle-path-10" data-name="Rectangle-path" class="cls-3" x="19" y="5" width="2" height="7"/>
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
src/images/theme.png
Normal file
|
After Width: | Height: | Size: 420 B |
BIN
src/images/translate.png
Normal file
|
After Width: | Height: | Size: 549 B |
BIN
src/images/tts.png
Normal file
|
After Width: | Height: | Size: 501 B |
|
|
@ -1,26 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<defs>
|
||||
|
||||
<style>.cls-1{fill:#669df6;}.cls-2{fill:#4285f4;}</style>
|
||||
|
||||
</defs>
|
||||
|
||||
<title>Icon_24px_TexttoSpeech_Color</title>
|
||||
|
||||
<g data-name="Product Icons">
|
||||
|
||||
<rect class="cls-1" x="2.01" y="6.99" width="6.64" height="1.67"/>
|
||||
|
||||
<polygon class="cls-1" points="11.17 15.32 2 15.32 2 16.99 2 16.99 11.17 16.99 11.17 15.32"/>
|
||||
|
||||
<polygon class="cls-1" points="4.51 11.16 2 11.16 2 12.82 6.18 12.82 12 12.82 10.33 11.16 4.51 11.16"/>
|
||||
|
||||
<path class="cls-2" d="M12,9.07a.42.42,0,0,1,.42-.36.41.41,0,0,1,.41.36v9.18a2.09,2.09,0,0,0,2.61,2A2.16,2.16,0,0,0,17,18.14V5.75a.4.4,0,0,1,.19-.4.41.41,0,0,1,.45,0,.4.4,0,0,1,.19.4v9.16a2.07,2.07,0,0,0,.81,1.64,2,2,0,0,0,1.8.37A2.16,2.16,0,0,0,22,14.8V12H20.33v2.92a.4.4,0,0,1-.19.4.41.41,0,0,1-.45,0,.4.4,0,0,1-.19-.4V5.75a2.09,2.09,0,0,0-2.61-2,2.16,2.16,0,0,0-1.56,2.13V18.25a.4.4,0,0,1-.19.4.41.41,0,0,1-.45,0,.4.4,0,0,1-.19-.4V9.08a2.07,2.07,0,0,0-4.11-.36,2.4,2.4,0,0,0-.05.46v2L12,12.82V9.07Z"/>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
src/images/twitch.png
Normal file
|
After Width: | Height: | Size: 436 B |
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="24px" height="24px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none">
|
||||
|
||||
<path fill="#ffffff" d="M13 7.5l-2 2H9l-1.75 1.75V9.5H5V2h8v5.5z"/>
|
||||
|
||||
<g fill="#9146FF">
|
||||
|
||||
<path d="M4.5 1L2 3.5v9h3V15l2.5-2.5h2L14 8V1H4.5zM13 7.5l-2 2H9l-1.75 1.75V9.5H5V2h8v5.5z"/>
|
||||
|
||||
<path d="M11.5 3.75h-1v3h1v-3zM8.75 3.75h-1v3h1v-3z"/>
|
||||
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 474 B |
455
src/index.html
|
|
@ -29,36 +29,33 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="toasts"></div>
|
||||
<!--#region title bar -->
|
||||
<header id="titlebar">
|
||||
<div id="drag-region">
|
||||
<div class="language-selector">
|
||||
<span class="flag-icon" id="selected-flag">🇬🇧</span>
|
||||
<span id="selected-language">EN</span>
|
||||
<div class="language-dropdown" id="languageDropdown">
|
||||
<div class="language-item" language="NL" flag="🇳🇱">🇳🇱 NL</div>
|
||||
<div class="language-item" language="ES" flag="🇪🇸">🇪🇸 ES</div>
|
||||
<div class="language-item" language="EN" flag="🇬🇧">🇬🇧 EN</div>
|
||||
</div>
|
||||
<div id="BtnInfo" class="about" tip="About">
|
||||
<!-- <i class="fa-solid fa-microphone-lines"></i> -->
|
||||
<img class="AdvancedMenuIcon2" src="./images/icon-256.png" alt=" " />
|
||||
</div>
|
||||
<div class="language-selector" tip="Selected language">
|
||||
<i class="fa-solid fa-globe"></i>
|
||||
<select name="defaultLanguage" class="top-select" id="language"></select>
|
||||
</div>
|
||||
<div id="window-controls">
|
||||
<div class="button" id="min-button">
|
||||
<div class="button" id="min-button" tip="Minimize window" tip-left>
|
||||
<i class="fa-solid fa-window-minimize"></i>
|
||||
</div>
|
||||
<div class="button" id="max-button">
|
||||
<div class="button" id="max-button" tip="Maximize window" tip-left>
|
||||
<i class="fa-solid fa-window-maximize"></i>
|
||||
</div>
|
||||
<div class="button" id="close-button">
|
||||
<div class="button" id="close-button" tip="Close application" tip-left>
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<!--#endregion -->
|
||||
|
||||
<div class="container">
|
||||
<!-- #region Notifications -->
|
||||
<div id="toasts"></div>
|
||||
<!--#endregion -->
|
||||
|
||||
<!--#region Menu, left section-->
|
||||
|
|
@ -68,17 +65,26 @@
|
|||
<li class="item item-active" tip="Chat" id="btnChat">
|
||||
<i class="fa fa-comments"></i>
|
||||
</li>
|
||||
<li class="item" tip="Logs" id="btnLogs">
|
||||
<i class="fa fa-clipboard menu-icon"></i>
|
||||
<li class="item" tip="Configuration" id="btnConfiguration">
|
||||
<i class="fa fa-cogs menu-icon"></i>
|
||||
</li>
|
||||
<li class="item" tip="Theme Creator" id="btnThemeCreator">
|
||||
<i class="fa-solid fa-palette"></i>
|
||||
</li>
|
||||
<li class="item" tip="Chat Creator" id="btnChatCreator">
|
||||
<i class="fa-solid fa-spray-can"></i>
|
||||
</li>
|
||||
<li class="item" tip="BrowserSource ChatBubble" id="btnBrowsersourceChat">
|
||||
<i class="fa fa-history menu-icon"></i>
|
||||
</li>
|
||||
<li class="item" tip="Vtuber" id="btnBrowsersourceVtuber">
|
||||
<li class="item" tip="BrowserSource Vtuber" id="btnBrowsersourceVtuber">
|
||||
<i class="fa fa-user menu-icon"></i>
|
||||
</li>
|
||||
<li class="item" tip="Configuration" id="btnConfiguration">
|
||||
<i class="fa fa-cogs menu-icon"></i>
|
||||
<li class="item" tip="Logs" id="btnLogs">
|
||||
<i class="fa fa-clipboard menu-icon"></i>
|
||||
</li>
|
||||
<li class="item active-mic" tip="Info" id="btnConfiguration">
|
||||
<i class="fa-solid fa-microphone-lines"></i>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
@ -120,39 +126,34 @@
|
|||
<div class="OptionPanel" id="Configuration">
|
||||
<div id="tstx">
|
||||
<div>
|
||||
<div class="optionrow">
|
||||
<button class="AdvancedMenuButton" id="ShowThemeCreator">Open Theme Creator</button>
|
||||
</div>
|
||||
|
||||
<div class="SaveConfig">
|
||||
<fieldset id="NotificationMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable notification sounds">
|
||||
<img class="AdvancedMenuIcon" src="./images/tts.svg" alt=" " />
|
||||
<div class="AdvancedMenuLabel">Enable TTS</div>
|
||||
<input type="checkbox" id="USE_TTS" class="checkbox" />
|
||||
<label for="USE_TTS" class="toggle-small "></label>
|
||||
<fieldset id="STTMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="General settings">
|
||||
<img class="AdvancedMenuIcon" src="./images/settings.png" alt=" " />
|
||||
<div class="AdvancedMenuLabel3">General settings</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">Primary TTS Voice</div>
|
||||
<select name="primaryVoice" id="primaryVoice"></select>
|
||||
<select
|
||||
name="language"
|
||||
class="language"
|
||||
id="primaryLanguage"
|
||||
tip="Primary language to detect in chat"
|
||||
></select>
|
||||
<div class="AdvancedMenuRow inputServer">
|
||||
<div class="AdvancedMenuLabel">Port</div>
|
||||
<input type="text" class="fname inputServer" id="PORT" />
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
id="Info_PORT"
|
||||
tip="Port to use to host additional services"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">Secondary TTS Voice</div>
|
||||
<select name="secondaryVoice" id="secondaryVoice"></select>
|
||||
<select
|
||||
name="language"
|
||||
class="language"
|
||||
id="secondaryLanguage"
|
||||
tip="Secondary language to detect in chat"
|
||||
></select>
|
||||
<div class="AdvancedMenuRow">
|
||||
<div class="AdvancedMenuLabel">Default TTS Service</div>
|
||||
<select class="menu-select" name="primaryTTSService" id="primaryTTSService"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuRow">
|
||||
<div class="AdvancedMenuLabel">2<sup>nd</sup> TTS Service</div>
|
||||
<select class="menu-select" name="secondaryTTSService" id="secondaryTTSService"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<div class="AdvancedMenuLabel">TTS Output Device</div>
|
||||
<select class="menu-select" name="ttsAudioDevice" id="ttsAudioDevice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<div class="AdvancedMenuLabel">TTS Volume</div>
|
||||
<div class="slider-container">
|
||||
<input id="ttsVolumeSlider" class="styled-slider slider-progress1" type="range" />
|
||||
|
|
@ -162,25 +163,116 @@
|
|||
<input type="text" id="ttsVolume" class="inputBox" />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="STTMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable STT">
|
||||
<img class="AdvancedMenuIcon" src="./images/stt.png" alt=" " />
|
||||
<input type="checkbox" id="USE_STT" class="checkbox" />
|
||||
<label for="USE_STT" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable STT</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputSTT">
|
||||
<div class="AdvancedMenuLabel">STT Input Device</div>
|
||||
<select class="menu-select" name="microphone" id="microphone"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow voiceLanguageDetection inputSTT">
|
||||
<div class="AdvancedMenuLabel">Voice Language</div>
|
||||
<select class="menu-select" name="sttModel" id="sttModel" tip="Language Service to use"></select>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="AdvancedMenuGoogle" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable Language service">
|
||||
<img class="AdvancedMenuIcon" src="./images/translate.png" alt />
|
||||
<input type="checkbox" id="USE_DETECTION" class="checkbox" />
|
||||
<label for="USE_DETECTION" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable Language detection</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow languageDetectionInput">
|
||||
<div class="AdvancedMenuLabel">Language detection service</div>
|
||||
<select
|
||||
class="menu-select"
|
||||
name="language"
|
||||
id="languageService"
|
||||
tip="Language Service to use"
|
||||
></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow languageDetectionInput">
|
||||
<div class="AdvancedMenuLabel">Translate incoming chat messages to</div>
|
||||
<label for="USE_CHAT_LANGUAGE_DETECTION" class="toggle-small "></label>
|
||||
<input type="checkbox" id="USE_CHAT_LANGUAGE_DETECTION" class="checkbox" />
|
||||
<select
|
||||
class="menu-select"
|
||||
name="language"
|
||||
id="translateChatMessageLanguage"
|
||||
tip="Language Service to use"
|
||||
></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow languageDetectionInput">
|
||||
<div class="AdvancedMenuLabel">Default TTS language</div>
|
||||
<select
|
||||
class="menu-select"
|
||||
name="defaultLanguage"
|
||||
id="defaultLanguage"
|
||||
tip="Language Service to use"
|
||||
></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow languageDetectionInput">
|
||||
<div class="AdvancedMenuLabel">2<sup>nd</sup> TTS language</div>
|
||||
<select
|
||||
class="menu-select"
|
||||
name="secondaryLanguage"
|
||||
id="secondaryLanguage"
|
||||
tip="Language Service to use"
|
||||
></select>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="TTSMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable TTS">
|
||||
<img class="AdvancedMenuIcon" src="./images/tts.png" alt=" " />
|
||||
<input type="checkbox" id="USE_TTS" class="checkbox" />
|
||||
<label for="USE_TTS" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable internal TTS</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">Test TTS</div>
|
||||
<textarea id="TTSTest">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test TTS">
|
||||
<i class="fa fa-play-circle fa-2x SmallButton option-icon-container" id="TTSTestButton"></i>
|
||||
<div class="AdvancedMenuLabel">Default Internal Voice</div>
|
||||
<select class="menu-select" name="primaryVoice" id="primaryVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">Test default Internal Voice</div>
|
||||
<textarea id="testPrimaryTTS">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i
|
||||
class="fa fa-play-circle fa-2x SmallButton option-icon-container"
|
||||
id="TestDefaultTTSButton"
|
||||
></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">TTS Output Device</div>
|
||||
<select name="ttsAudioDevice" id="ttsAudioDevice"></select>
|
||||
<div class="AdvancedMenuLabel">2<sup>nd</sup> Internal Voice</div>
|
||||
<select class="menu-select" name="secondaryVoice" id="secondaryVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputTTS">
|
||||
<div class="AdvancedMenuLabel">Test 2<sup>nd</sup> Internal Voice</div>
|
||||
<textarea id="testSecondaryTTS">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i
|
||||
class="fa fa-play-circle fa-2x SmallButton option-icon-container"
|
||||
id="TestSecondaryTTSButton"
|
||||
></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset id="NotificationMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable notification sounds">
|
||||
<img class="AdvancedMenuIcon" src="./images/note.svg" alt=" " />
|
||||
<div class="AdvancedMenuLabel">Enable notification sounds</div>
|
||||
<img class="AdvancedMenuIcon" src="./images/sound.png" alt=" " />
|
||||
<input type="checkbox" id="USE_NOTIFICATION_SOUNDS" class="checkbox" />
|
||||
<label for="USE_NOTIFICATION_SOUNDS" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable notification sounds</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputNotificationSound">
|
||||
<div class="AdvancedMenuLabel">Notification Volume</div>
|
||||
|
|
@ -194,7 +286,7 @@
|
|||
</div>
|
||||
<div class="AdvancedMenuRow inputNotificationSound">
|
||||
<div class="AdvancedMenuLabel">Notification Sound</div>
|
||||
<select name="notification" id="notification"></select>
|
||||
<select class="menu-select" name="notification" id="notification"></select>
|
||||
<div class="option-icon-container">
|
||||
<i
|
||||
class="fa fa-play-circle fa-2x SmallButton option-icon-container"
|
||||
|
|
@ -205,13 +297,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="TwitchMenu" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable Twitch">
|
||||
<img class="AdvancedMenuIcon" src="./images/twitch.svg" alt=" " />
|
||||
|
||||
<div class="AdvancedMenuLabel">Enable Twitch</div>
|
||||
<img class="AdvancedMenuIcon" src="./images/twitch.png" alt=" " />
|
||||
<input type="checkbox" id="USE_TWITCH" class="checkbox" />
|
||||
<label for="USE_TWITCH" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable Twitch</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputTwitch">
|
||||
<div class="AdvancedMenuLabel">Channel Name</div>
|
||||
|
|
@ -224,9 +316,15 @@
|
|||
</div>
|
||||
<div class="AdvancedMenuRow inputTwitch">
|
||||
<div class="AdvancedMenuLabel">Oauth Token</div>
|
||||
<input type="password" type2="text" class="fname inputTwitch" id="TWITCH_OAUTH_TOKEN" />
|
||||
<input
|
||||
type="password"
|
||||
type2="text"
|
||||
class="fname inputTwitch"
|
||||
id="TWITCH_OAUTH_TOKEN"
|
||||
placeholder="click the ? icon to get the OAuth token"
|
||||
/>
|
||||
<button class="password-toggle-btn password-toggle-btn1">
|
||||
<span class="password-toggle-icon">👁️</span>
|
||||
<span class="password-toggle-icon"><i class="fa-regular fa-eye-slash"></i></span>
|
||||
</button>
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
|
|
@ -244,22 +342,14 @@
|
|||
></i>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="AdvancedMenuServer" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable Server">
|
||||
<img class="AdvancedMenuIcon" src="./images/server.svg" alt />
|
||||
<div class="AdvancedMenuLabel">Enable Server</div>
|
||||
<input type="checkbox" id="USE_SERVER" class="checkbox" />
|
||||
<label for="USE_SERVER" class="toggle-small "></label>
|
||||
<img class="AdvancedMenuIcon" src="./images/server.png" alt />
|
||||
<input type="checkbox" id="USE_MODULES" class="checkbox" />
|
||||
<label for="USE_MODULES" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable Modules</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputServer">
|
||||
<div class="AdvancedMenuLabel">Port</div>
|
||||
<input type="text" class="fname inputServer" id="PORT" />
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
id="Info_PORT"
|
||||
tip="Port to use to host additional services"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputServer">
|
||||
<div class="AdvancedMenuLabel">Use Vtuber</div>
|
||||
<input type="checkbox" id="USE_VTUBER" class="checkbox" />
|
||||
|
|
@ -283,19 +373,20 @@
|
|||
></i>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="AdvancedMenuAmazon" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable Amazon">
|
||||
<img class="AdvancedMenuIcon" src="./images/amazon.svg" alt />
|
||||
<div class="AdvancedMenuLabel">Enable Amazon TTS</div>
|
||||
<img class="AdvancedMenuIcon" src="./images/amazon.png" alt />
|
||||
<input type="checkbox" id="USE_AMAZON" class="checkbox" />
|
||||
<label for="USE_AMAZON" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable Amazon services</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">Access Key</div>
|
||||
<input type="password" type2="text" class="fname inputAmazon" id="AMAZON_ACCESS_KEY" /><button
|
||||
class="password-toggle-btn password-toggle-btn4"
|
||||
>
|
||||
<span class="password-toggle-icon">👁️</span>
|
||||
<span class="password-toggle-icon"><i class="fa-regular fa-eye-slash"></i></span>
|
||||
</button>
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
|
|
@ -308,7 +399,7 @@
|
|||
<input type="password" type2="text" class="fname inputAmazon" id="AMAZON_ACCESS_SECRET" /><button
|
||||
class="password-toggle-btn password-toggle-btn5"
|
||||
>
|
||||
<span class="password-toggle-icon">👁️</span>
|
||||
<span class="password-toggle-icon"><i class="fa-regular fa-eye-slash"></i></span>
|
||||
</button>
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
|
|
@ -325,20 +416,54 @@
|
|||
tip="Test Amazon credentials"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">Characters used</div>
|
||||
<input type="text" class="fname inputAmazon" id="CharactersUsedAmazon" />
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
id="Info_Characters_Amazon"
|
||||
tip="Characters that already have been used in Amazon TTS service"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">Default Amazon Voice</div>
|
||||
<select class="menu-select" name="primaryAmazonVoice" id="primaryAmazonVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">Test Default Amazon Voice</div>
|
||||
<textarea id="testPrimaryAmazonVoice">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i class="fa fa-play-circle fa-2x SmallButton option-icon-container" id="TTSTestButton"></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">2<sup>nd</sup> Amazon Voice</div>
|
||||
<select class="menu-select" name="secondaryAmazonVoice" id="secondaryAmazonVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputAmazon">
|
||||
<div class="AdvancedMenuLabel">Test 2<sup>nd</sup> Amazon Voice</div>
|
||||
<textarea id="testSecondaryAmazonVoice">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i class="fa fa-play-circle fa-2x SmallButton option-icon-container" id="TTSTestButton"></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="AdvancedMenuGoogle" class="AdvancedMenu">
|
||||
<legend class="legendStyle" tip="Enable/Disable Google service">
|
||||
<img class="AdvancedMenuIcon" src="./images/google.svg" alt />
|
||||
<div class="AdvancedMenuLabel">Enable Google TTS</div>
|
||||
<img class="AdvancedMenuIcon" src="./images/google.png" alt />
|
||||
<input type="checkbox" id="USE_GOOGLE" class="checkbox" />
|
||||
<label for="USE_GOOGLE" class="toggle-small "></label>
|
||||
<div class="AdvancedMenuLabel3">Enable Google services</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">API Key</div>
|
||||
<input type="password" type2="text" class="fname inputGoogle" id="GOOGLE_API_KEY" /><button
|
||||
class="password-toggle-btn password-toggle-btn6"
|
||||
>
|
||||
<span class="password-toggle-icon">👁️</span>
|
||||
<span class="password-toggle-icon"><i class="fa-regular fa-eye-slash"></i></span>
|
||||
</button>
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
|
|
@ -355,70 +480,41 @@
|
|||
tip="Test Google credentials"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">Characters used</div>
|
||||
<input type="text" class="fname inputGoogle" id="CharactersUsedGoogle" />
|
||||
<i
|
||||
class="fa fa-question-circle fa-2x SmallButton option-icon-container"
|
||||
id="Info_Characters_Google"
|
||||
tip="Characters that already have been used in Google TTS service"
|
||||
></i>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">Default Google Voice</div>
|
||||
<select class="menu-select" name="primaryGoogleVoice" id="primaryGoogleVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">Test default Google Voice</div>
|
||||
<textarea id="testPrimaryGoogleVoice">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i class="fa fa-play-circle fa-2x SmallButton option-icon-container" id="TTSTestButton"></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">2<sup>nd</sup> Google Voice</div>
|
||||
<select class="menu-select" name="secondaryGoogleVoice" id="secondaryGoogleVoice"></select>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow inputGoogle">
|
||||
<div class="AdvancedMenuLabel">Test 2<sup>nd</sup> Google Voice</div>
|
||||
<textarea id="testSecondaryGoogleVoice">Hi, This is a test</textarea>
|
||||
<div class="option-icon-container" tip="Test internal TTS">
|
||||
<i class="fa fa-play-circle fa-2x SmallButton option-icon-container" id="TTSTestButton"></i>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="ThemeCreator_mask">
|
||||
<div class="SaveConfig">
|
||||
<div class="AdvancedMenuRow">
|
||||
<button class="AdvancedMenuButton" id="HideThemeCreator">Close Theme Creator</button>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
|
||||
<div id="mini-container">
|
||||
<div id="mini-top-bar"></div>
|
||||
<div id="mini-main">
|
||||
<div id="mini-left"></div>
|
||||
<div id="mini-mid"></div>
|
||||
<div id="mini-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend class="legendStyle" tip="Enable/Disable Custom Theme">
|
||||
<img class="AdvancedMenuIcon" src="./images/theme.svg" alt />
|
||||
<div class="AdvancedMenuLabel">Enable Custom Theme</div>
|
||||
<input type="checkbox" id="USE_CUSTOM_THEME" class="checkbox" />
|
||||
<label for="USE_CUSTOM_THEME" class="toggle-small"></label>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_1" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 1</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_2" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 2</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_3" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 3</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_4" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 4</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="TOP_BAR" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Top Bar</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MID_SECTION" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Mid Section</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_BG" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Background</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_HEADER" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Header</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_MESSAGE" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Message</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -439,6 +535,71 @@
|
|||
</div>
|
||||
</div>
|
||||
<!--#endregion -->
|
||||
|
||||
<div class="OptionPanel" id="ThemeCreator">
|
||||
<div class="SaveConfig">
|
||||
<div class="AdvancedMenuRow">
|
||||
<button class="AdvancedMenuButton" id="HideThemeCreator">Close Theme Creator</button>
|
||||
<label class="testLabel Basiclabel"></label>
|
||||
</div>
|
||||
|
||||
<div id="mini-container">
|
||||
<div id="mini-top-bar"></div>
|
||||
<div id="mini-main">
|
||||
<div id="mini-left"></div>
|
||||
<div id="mini-mid"></div>
|
||||
<div id="mini-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend class="legendStyle" tip="Enable/Disable Custom Theme">
|
||||
<img class="AdvancedMenuIcon" src="./images/theme.png" alt />
|
||||
<input type="checkbox" id="USE_CUSTOM_THEME" class="checkbox" />
|
||||
<label for="USE_CUSTOM_THEME" class="toggle-small"></label>
|
||||
<div class="AdvancedMenuLabel">Enable Custom Theme</div>
|
||||
</legend>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_1" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 1</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_2" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 2</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_3" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 3</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MAIN_COLOR_4" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Main Color 4</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="TOP_BAR" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Top Bar</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="MID_SECTION" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Mid Section</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_BG" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Background</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_HEADER" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Header</div>
|
||||
</div>
|
||||
<div class="AdvancedMenuRow">
|
||||
<input type="color" id="CHAT_BUBBLE_MESSAGE" value="#0000ff" />
|
||||
<div class="AdvancedMenuLabel2">Chat Bubble Message</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="OptionPanel" id="ChatCreator"></div>
|
||||
</div>
|
||||
<!--#endregion -->
|
||||
|
||||
|
|
@ -446,8 +607,8 @@
|
|||
<div class="sidepanel-right">
|
||||
<div class="viewer-list">
|
||||
<div class="tabx-bar">
|
||||
<a class="tabx" href="#" data-text="Twitch">
|
||||
<img class="tab" src="./images/twitch.svg" alt=" " />
|
||||
<a class="AdvancedMenuIcon" href="#" data-text="Twitch">
|
||||
<img class="tab" src="./images/twitch.png" alt=" " />
|
||||
</a>
|
||||
</div>
|
||||
<section id="content1">
|
||||
|
|
@ -472,7 +633,7 @@
|
|||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div tip="Hide Viewers" tip-left>
|
||||
<div id="VIEWERS_PANEL" tip="Hide Viewers" tip-left>
|
||||
<div class="circle-right">
|
||||
<i class="fa fa-eye hide" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
|
|
|||
192
src/js/amazon.js
|
|
@ -2,94 +2,118 @@ const https = require('https');
|
|||
const querystring = require('querystring');
|
||||
const aws4 = require('aws4');
|
||||
|
||||
class PollyTTS {
|
||||
constructor(credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
function getAmazonVoices() {
|
||||
if (!settings.AMAZON.USE_AMAZON) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
textToSpeech(options, callback) {
|
||||
if (!options) {
|
||||
return callback(new Error('Options are missing'));
|
||||
}
|
||||
const qs = {
|
||||
Text: options.text,
|
||||
TextType: options.textType || 'text',
|
||||
VoiceId: options.voiceId || 'Vicki',
|
||||
SampleRate: options.sampleRate || 22050,
|
||||
OutputFormat: options.outputFormat || 'mp3',
|
||||
};
|
||||
const opts = {
|
||||
service: 'polly',
|
||||
region: options.region || 'eu-west-1',
|
||||
path: `/v1/speech?${querystring.stringify(qs)}`,
|
||||
signQuery: true,
|
||||
};
|
||||
addVoiceService('Amazon');
|
||||
|
||||
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
|
||||
aws4.sign(opts, this.credentials);
|
||||
https
|
||||
.get(opts, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(
|
||||
new Error(`Request Failed. Status Code: ${res.statusCode}`),
|
||||
);
|
||||
}
|
||||
callback(null, res);
|
||||
return true;
|
||||
})
|
||||
.on('error', (e) => {
|
||||
callback(e);
|
||||
});
|
||||
let primaryVoice = document.querySelector('#primaryAmazonVoice');
|
||||
let secondaryVoice = document.querySelector('#secondaryAmazonVoice');
|
||||
|
||||
return null;
|
||||
}
|
||||
function setVoicesinSelect(voiceSelect) {
|
||||
const voices = Object.values(amazonVoices);
|
||||
voices.forEach((voice) => {
|
||||
const option = document.createElement('option');
|
||||
option.classList.add('option');
|
||||
|
||||
describeVoices(options, callback) {
|
||||
if (!options) {
|
||||
return callback(new Error('Options are missing'));
|
||||
}
|
||||
const qs = {};
|
||||
option.value = voice;
|
||||
option.innerHTML = voice;
|
||||
|
||||
if (options.languageCode) {
|
||||
qs.LanguageCode = options.languageCode;
|
||||
}
|
||||
|
||||
if (options.nextToken) {
|
||||
qs.NextToken = options.nextToken;
|
||||
}
|
||||
|
||||
const opts = {
|
||||
service: 'polly',
|
||||
region: options.region || 'eu-west-1',
|
||||
path: `/v1/voices?${querystring.stringify(qs)}`,
|
||||
signQuery: true,
|
||||
};
|
||||
|
||||
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
|
||||
aws4.sign(opts, this.credentials);
|
||||
https
|
||||
.get(opts, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(
|
||||
new Error(`Request Failed. Status Code: ${res.statusCode}`),
|
||||
);
|
||||
}
|
||||
let body = '';
|
||||
res.on('readable', () => {
|
||||
body += res.read();
|
||||
});
|
||||
res.on('end', () => {
|
||||
callback(null, body);
|
||||
});
|
||||
|
||||
return undefined;
|
||||
})
|
||||
.on('error', (e) => {
|
||||
callback(e);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
voiceSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
setVoicesinSelect(primaryVoice);
|
||||
primaryVoice.value = settings.AMAZON.PRIMARY_VOICE;
|
||||
setVoicesinSelect(secondaryVoice);
|
||||
secondaryVoice.value = settings.AMAZON.SECONDARY_VOICE;
|
||||
}
|
||||
|
||||
module.exports = PollyTTS;
|
||||
if (settings.AMAZON.USE_AMAZON) {
|
||||
getAmazonVoices();
|
||||
}
|
||||
|
||||
class PollyTTS {
|
||||
constructor() {}
|
||||
|
||||
textToSpeech(options, callback) {
|
||||
if (!options) {
|
||||
return callback(new Error('Options are missing'));
|
||||
}
|
||||
|
||||
const qs = {
|
||||
Text: options.text,
|
||||
TextType: options.textType || 'text',
|
||||
VoiceId: options.voiceId || 'Mia',
|
||||
SampleRate: options.sampleRate || 22050,
|
||||
OutputFormat: options.outputFormat || 'mp3',
|
||||
Engine: options.engine || 'neural',
|
||||
};
|
||||
|
||||
const opts = {
|
||||
service: 'polly',
|
||||
region: options.region || 'us-east-1',
|
||||
path: `/v1/speech?${querystring.stringify(qs)}`,
|
||||
signQuery: true,
|
||||
};
|
||||
|
||||
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
|
||||
aws4.sign(opts, this.credentials);
|
||||
https
|
||||
.get(opts, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
|
||||
}
|
||||
callback(null, res);
|
||||
return true;
|
||||
})
|
||||
.on('error', (e) => {
|
||||
callback(e);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
describeVoices(callback, credentials) {
|
||||
this.credentials = credentials;
|
||||
const qs = {
|
||||
Engine: 'neural',
|
||||
};
|
||||
|
||||
const opts = {
|
||||
service: 'polly',
|
||||
region: 'us-east-1',
|
||||
path: `/v1/voices?${querystring.stringify(qs)}`,
|
||||
signQuery: true,
|
||||
};
|
||||
|
||||
// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
|
||||
aws4.sign(opts, this.credentials);
|
||||
https
|
||||
.get(opts, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(new Error(`Request Failed. Status Code: ${res.statusCode}`));
|
||||
}
|
||||
|
||||
let body = '';
|
||||
res.on('readable', () => {
|
||||
body += res.read();
|
||||
});
|
||||
res.on('end', () => {
|
||||
callback(null, body);
|
||||
});
|
||||
|
||||
return undefined;
|
||||
})
|
||||
.on('error', (e) => {
|
||||
callback(e);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const pollyTTS = new PollyTTS();
|
||||
module.exports = pollyTTS;
|
||||
|
|
|
|||
126
src/js/auth.js
|
|
@ -1,65 +1,81 @@
|
|||
console.log("kees");
|
||||
// const clientId = 'YOUR_TWITCH_CLIENT_ID';
|
||||
// const redirectUri = 'http://localhost:1989/auth';
|
||||
// const scopes = ['chat:edit', 'chat:read'];
|
||||
const twitchAuthentication = () =>
|
||||
new Promise((resolve) => {
|
||||
const http = require('http');
|
||||
const redirectUri = 'http://localhost:1989/auth';
|
||||
const scopes = ['chat:edit', 'chat:read'];
|
||||
|
||||
// const express = require('express');
|
||||
// const tempAuthServer = express();
|
||||
// const port = 1989;
|
||||
const express = require('express');
|
||||
let tempAuthServer = express();
|
||||
const port = 1989;
|
||||
|
||||
// const { parse: parseQueryString } = require('querystring');
|
||||
const { parse: parseQueryString } = require('querystring');
|
||||
|
||||
// tempAuthServer.use(function (req, res, next) {
|
||||
// if (req.url !== "/auth") {
|
||||
// let token = parseQueryString(req.query.auth)
|
||||
// res.json(token["#access_token"]);
|
||||
// // settings.TWITCH.OAUTH_TOKEN = token["#access_token"];
|
||||
// // fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
// // settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
// // tempAuthServer.close();
|
||||
// }
|
||||
// next();
|
||||
// });
|
||||
tempAuthServer.use(function (req, res, next) {
|
||||
if (req.url !== '/auth') {
|
||||
let token = parseQueryString(req.query.auth);
|
||||
settings.TWITCH.OAUTH_TOKEN = token['#access_token'];
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
|
||||
// const htmlString = `
|
||||
// <!DOCTYPE html>
|
||||
// <html>
|
||||
// <head>
|
||||
// <title>Authentication</title>
|
||||
// </head>
|
||||
// <body>
|
||||
// <h1>Authentication successful! You can close this window now.</h1>
|
||||
// <form name="auth" "action="auth" method="get" >
|
||||
// <input type="text" id="auth" name="auth"/>
|
||||
// <input type="submit" />
|
||||
// </form>
|
||||
// </body>
|
||||
// </html>
|
||||
// <script>
|
||||
// function onSubmitComplete() {
|
||||
// close();
|
||||
// }
|
||||
// document.querySelector("#auth").value = document.location.hash;
|
||||
// document.auth.submit();
|
||||
// setTimeout(onSubmitComplete, 500);
|
||||
// </script>`;
|
||||
resolve(token['#access_token']);
|
||||
stopServer();
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// tempAuthServer.get('/auth', (req, res) => {
|
||||
// // res.send(htmlString);
|
||||
// });
|
||||
function stopServer() {
|
||||
tempAuthServer.close();
|
||||
}
|
||||
|
||||
// tempAuthServer.post('/auth', (req, res) => {
|
||||
// res.render('authentication', { name: req.body.name });
|
||||
// });
|
||||
const htmlString = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Authentication</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Authentication successful! You can close this window now.</h1>
|
||||
<form name="auth" "action="auth" method="get" >
|
||||
<input type="text" id="auth" name="auth"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
function onSubmitComplete() {
|
||||
close();
|
||||
}
|
||||
document.querySelector("#auth").style.display="none";
|
||||
document.querySelector("#auth").value = document.location.hash;
|
||||
document.auth.submit();
|
||||
setTimeout(onSubmitComplete, 500);
|
||||
</script>
|
||||
`;
|
||||
|
||||
// tempAuthServer.listen(port, () => { });
|
||||
tempAuthServer.get('/auth', (req, res) => {
|
||||
res.send(htmlString);
|
||||
});
|
||||
|
||||
// const authURL = `https://id.twitch.tv/oauth2/authorize?client_id=${settings.TWITCH.CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=token&scope=${scopes.join(' ')}`;
|
||||
// shell.openExternal(authURL);
|
||||
onsole.log('ExecPath', process.execPath);
|
||||
tempAuthServer.post('/auth', (req, res) => {
|
||||
res.render('authentication', { name: req.body.name });
|
||||
});
|
||||
|
||||
process.on('message', (m) => {
|
||||
console.log('Got message:', m);
|
||||
const server = http.createServer(tempAuthServer);
|
||||
|
||||
process.send("message", "lol");
|
||||
});
|
||||
server.listen(port, () => {
|
||||
const authURL = `https://id.twitch.tv/oauth2/authorize?client_id=${settings.TWITCH.CLIENT_ID}&redirect_uri=${encodeURIComponent(
|
||||
redirectUri,
|
||||
)}&response_type=token&scope=${scopes.join(' ')}`;
|
||||
shell.openExternal(authURL);
|
||||
});
|
||||
|
||||
function stopServer() {
|
||||
server.close(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
function getTwitchOauthToken() {
|
||||
return twitchAuthentication().then((res) => {
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { getTwitchOauthToken };
|
||||
|
|
|
|||
162
src/js/backend.js
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
const spawn = require('child_process').spawn;
|
||||
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('Response:', responseData);
|
||||
internalVoices = responseData;
|
||||
} else {
|
||||
console.error('Failed to send termination signal to Flask server.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending termination signal:', error);
|
||||
}
|
||||
|
||||
let primaryVoice = document.querySelector('#primaryVoice');
|
||||
let 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;
|
||||
}
|
||||
|
||||
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('Response:', 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('Response:', 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(pythonPath, './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}`);
|
||||
resolve('finished'); // cannot get it to resolve with stdout
|
||||
});
|
||||
|
||||
// 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) {
|
||||
startSTT();
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error during backend initialization:', error);
|
||||
}
|
||||
}
|
||||
|
||||
initiateBackend();
|
||||
|
||||
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);
|
||||
} else {
|
||||
console.error('Failed to send termination signal to Flask server.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending termination signal:', error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = { getInternalTTSAudio };
|
||||
188
src/js/chat.js
|
|
@ -1,46 +1,46 @@
|
|||
function getResponse() {
|
||||
const userText = document.querySelector('#textInput').value;
|
||||
const userText = document.querySelector('#textInput').value;
|
||||
|
||||
// If nothing is written don't do anything
|
||||
if (userText === '') {
|
||||
return;
|
||||
}
|
||||
// If nothing is written don't do anything
|
||||
if (userText === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat message from received data
|
||||
const article = document.createElement('article');
|
||||
article.className = 'msg-container msg-self';
|
||||
// Create chat message from received data
|
||||
const article = document.createElement('article');
|
||||
article.className = 'msg-container msg-self';
|
||||
|
||||
article.innerHTML = messageTemplates.userTemplate;
|
||||
article.innerHTML = messageTemplates.userTemplate;
|
||||
|
||||
const postTime = article.querySelector('.post-time');
|
||||
if (postTime) {
|
||||
postTime.innerText = getPostTime();
|
||||
}
|
||||
const postTime = article.querySelector('.post-time');
|
||||
if (postTime) {
|
||||
postTime.innerText = getPostTime();
|
||||
}
|
||||
|
||||
const msg = article.querySelector('.msg');
|
||||
if (msg) {
|
||||
msg.innerText = userText;
|
||||
}
|
||||
const msg = article.querySelector('.msg');
|
||||
if (msg) {
|
||||
msg.innerText = userText;
|
||||
}
|
||||
|
||||
// Appends the message to the main chat box (shows the message)
|
||||
showChatMessage(article);
|
||||
// Appends the message to the main chat box (shows the message)
|
||||
showChatMessage(article);
|
||||
|
||||
twitch.sendMessage(userText);
|
||||
twitch.sendMessage(userText);
|
||||
|
||||
// Empty input box after sending message
|
||||
document.body.querySelector('#textInput').value = '';
|
||||
// Empty input box after sending message
|
||||
document.body.querySelector('#textInput').value = '';
|
||||
}
|
||||
|
||||
// Function that will execute when you press 'enter' in the message box
|
||||
document.body.querySelector('#textInput').addEventListener('keydown', (e) => {
|
||||
if (e.which === 13) {
|
||||
getResponse();
|
||||
}
|
||||
if (e.which === 13) {
|
||||
getResponse();
|
||||
}
|
||||
});
|
||||
|
||||
// Function that will execute when you click the 'send' button
|
||||
document.body.querySelector('#SendButton').addEventListener('click', () => {
|
||||
getResponse();
|
||||
getResponse();
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
|
@ -49,68 +49,50 @@ document.body.querySelector('#SendButton').addEventListener('click', () => {
|
|||
|
||||
// Left panel
|
||||
document.body.querySelector('.circle-left').addEventListener('click', () => {
|
||||
const menu = document.body.querySelector('.sidepanel-left');
|
||||
const menu = document.body.querySelector('.sidepanel-left');
|
||||
|
||||
if (menu.classList.contains('collapse-menu-left')) {
|
||||
menu.classList.remove('collapse-menu-left');
|
||||
} else {
|
||||
menu.classList.add('collapse-menu-left');
|
||||
}
|
||||
if (menu.classList.contains('collapse-menu-left')) {
|
||||
menu.classList.remove('collapse-menu-left');
|
||||
} else {
|
||||
menu.classList.add('collapse-menu-left');
|
||||
}
|
||||
|
||||
const leftCircle = document.body.querySelector('.circle-left');
|
||||
const leftCircle = document.body.querySelector('.circle-left');
|
||||
|
||||
if (leftCircle.classList.contains('collapse-circle-left')) {
|
||||
leftCircle.classList.remove('collapse-circle-left');
|
||||
} else {
|
||||
leftCircle.classList.add('collapse-circle-left');
|
||||
}
|
||||
if (leftCircle.classList.contains('collapse-circle-left')) {
|
||||
leftCircle.classList.remove('collapse-circle-left');
|
||||
} else {
|
||||
leftCircle.classList.add('collapse-circle-left');
|
||||
}
|
||||
});
|
||||
|
||||
// right panel
|
||||
document.body.querySelector('.circle-right').addEventListener('click', () => {
|
||||
const menu = document.body.querySelector('.sidepanel-right');
|
||||
|
||||
if (menu.classList.contains('collapse-menu-right')) {
|
||||
menu.classList.remove('collapse-menu-right');
|
||||
} else {
|
||||
menu.classList.add('collapse-menu-right');
|
||||
}
|
||||
|
||||
const leftCircle = document.body.querySelector('.circle-right');
|
||||
|
||||
if (leftCircle.classList.contains('collapse-circle-right')) {
|
||||
leftCircle.classList.remove('collapse-circle-right');
|
||||
} else {
|
||||
leftCircle.classList.add('collapse-circle-right');
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Show panels
|
||||
|
||||
// TODO: animate Option panels
|
||||
// TODO : optimize show panels
|
||||
// Function that shows and hides the option panels. (TTS, Configuration, Commands)
|
||||
const displayPanel = (panelSelectorClass, panelSelectorID, btnSelectorID) => {
|
||||
const btn = document.querySelector(btnSelectorID);
|
||||
const panel = document.querySelector(panelSelectorID);
|
||||
const panels = document.querySelectorAll(panelSelectorClass);
|
||||
const btn = document.querySelector(btnSelectorID);
|
||||
const panel = document.querySelector(panelSelectorID);
|
||||
const panels = document.querySelectorAll(panelSelectorClass);
|
||||
|
||||
btn.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
panels.forEach((el) => {
|
||||
if (el === panel) return;
|
||||
el.classList.remove('show');
|
||||
});
|
||||
if (panel.classList.contains('show')) {
|
||||
// panel.classList.remove('show');
|
||||
} else {
|
||||
panel.classList.add('show');
|
||||
}
|
||||
}, {
|
||||
capture: true,
|
||||
});
|
||||
btn.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
event.stopPropagation();
|
||||
panels.forEach((el) => {
|
||||
if (el === panel) return;
|
||||
el.classList.remove('show');
|
||||
});
|
||||
if (panel.classList.contains('show')) {
|
||||
} else {
|
||||
panel.classList.add('show');
|
||||
}
|
||||
},
|
||||
{
|
||||
capture: true,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
displayPanel('.OptionPanel', '#Configuration', '#btnConfiguration');
|
||||
|
|
@ -118,27 +100,32 @@ displayPanel('.OptionPanel', '#Logs', '#btnLogs');
|
|||
displayPanel('.OptionPanel', '#BrowsersourceChat', '#btnBrowsersourceChat');
|
||||
displayPanel('.OptionPanel', '#BrowsersourceVtuber', '#btnBrowsersourceVtuber');
|
||||
displayPanel('.OptionPanel', '#Chat', '#btnChat');
|
||||
displayPanel('.OptionPanel', '#ThemeCreator', '#btnThemeCreator');
|
||||
displayPanel('.OptionPanel', '#ChatCreator', '#btnChatCreator');
|
||||
// #endregion
|
||||
|
||||
const displayPanelX = (panelSelectorClass, panelSelectorID, btnSelectorID) => {
|
||||
const btn = document.querySelector(btnSelectorID);
|
||||
const panel = document.querySelector(panelSelectorID);
|
||||
const panels = document.querySelectorAll(panelSelectorClass);
|
||||
const btn = document.querySelector(btnSelectorID);
|
||||
const panel = document.querySelector(panelSelectorID);
|
||||
const panels = document.querySelectorAll(panelSelectorClass);
|
||||
|
||||
btn.addEventListener('click', (event) => {
|
||||
event.stopPropagation();
|
||||
panels.forEach((el) => {
|
||||
if (el === panel) return;
|
||||
el.classList.remove('item-active');
|
||||
});
|
||||
if (panel.classList.contains('item-active')) {
|
||||
// panel.classList.remove('item-active');
|
||||
} else {
|
||||
panel.classList.add('item-active');
|
||||
}
|
||||
}, {
|
||||
capture: true,
|
||||
});
|
||||
btn.addEventListener(
|
||||
'click',
|
||||
(event) => {
|
||||
event.stopPropagation();
|
||||
panels.forEach((el) => {
|
||||
if (el === panel) return;
|
||||
el.classList.remove('item-active');
|
||||
});
|
||||
if (panel.classList.contains('item-active')) {
|
||||
} else {
|
||||
panel.classList.add('item-active');
|
||||
}
|
||||
},
|
||||
{
|
||||
capture: true,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
displayPanelX('.item', '#btnChat', '#btnChat');
|
||||
|
|
@ -146,20 +133,9 @@ displayPanelX('.item', '#btnBrowsersourceChat', '#btnBrowsersourceChat');
|
|||
displayPanelX('.item', '#btnBrowsersourceVtuber', '#btnBrowsersourceVtuber');
|
||||
displayPanelX('.item', '#btnLogs', '#btnLogs');
|
||||
displayPanelX('.item', '#btnConfiguration', '#btnConfiguration');
|
||||
displayPanelX('.item', '#btnThemeCreator', '#btnThemeCreator');
|
||||
displayPanelX('.item', '#btnChatCreator', '#btnChatCreator');
|
||||
|
||||
// #region Show/Hide Theme Creator
|
||||
document.body.querySelector('#ShowThemeCreator').addEventListener('click', () => {
|
||||
document.getElementById('ThemeCreator_mask').style.visibility = 'visible';
|
||||
});
|
||||
|
||||
document.body.querySelector('#HideThemeCreator').addEventListener('click', () => {
|
||||
document.getElementById('ThemeCreator_mask').style.visibility = 'hidden';
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Test/Save TTS
|
||||
document.body.querySelector('#TTSTestButton').addEventListener('click', () => {
|
||||
const text = document.getElementById('TTSTest').value;
|
||||
sound.playVoice(text, '', 'User', text);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
function getGoogleVoices() {
|
||||
if (!settings.GOOGLE.USE_GOOGLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
addVoiceService('Google');
|
||||
|
||||
let primaryVoice = document.querySelector('#primaryGoogleVoice');
|
||||
let secondaryVoice = document.querySelector('#secondaryGoogleVoice');
|
||||
|
||||
function setVoicesinSelect(voiceSelect) {
|
||||
const voices = Object.values(googleVoices);
|
||||
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.GOOGLE.PRIMARY_VOICE;
|
||||
setVoicesinSelect(secondaryVoice);
|
||||
secondaryVoice.value = settings.GOOGLE.SECONDARY_VOICE;
|
||||
}
|
||||
|
||||
if (settings.GOOGLE.USE_GOOGLE) {
|
||||
getGoogleVoices();
|
||||
}
|
||||
331
src/js/languages.js
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
// TODO: Enable STT:
|
||||
// Output STT to TTS? *TTS service selection* (for now, later add the option to choose a specific voice with mega dropdowns)
|
||||
// *automatic translation: make an translation.js and add ALL the texts and have it translated if user chooses a language in top bar
|
||||
// *info page with credits, version and more info
|
||||
|
||||
const languages = {
|
||||
acehnese: { IETF: 'ace-ID', 'ISO-639': 'ace' },
|
||||
afrikaans: { IETF: 'af-ZA', 'ISO-639': 'af' },
|
||||
akan: { IETF: 'ak-GH', 'ISO-639': 'ak' },
|
||||
albanian: { IETF: 'sq-AL', 'ISO-639': 'sq' },
|
||||
amharic: { IETF: 'am-ET', 'ISO-639': 'am' },
|
||||
'antigua and barbuda creole english': { IETF: 'aig-AG', 'ISO-639': 'aig' },
|
||||
arabic: { IETF: 'ar-SA', 'ISO-639': 'ar' },
|
||||
'arabic egyptian': { IETF: 'ar-EG', 'ISO-639': 'ar' },
|
||||
aragonese: { IETF: 'an-ES', 'ISO-639': 'an' },
|
||||
armenian: { IETF: 'hy-AM', 'ISO-639': 'hy' },
|
||||
assamese: { IETF: 'as-IN', 'ISO-639': 'as' },
|
||||
asturian: { IETF: 'ast-ES', 'ISO-639': 'ast' },
|
||||
'austrian german': { IETF: 'de-AT', 'ISO-639': 'de' },
|
||||
awadhi: { IETF: 'awa-IN', 'ISO-639': 'awa' },
|
||||
'ayacucho quechua': { IETF: 'quy-PE', 'ISO-639': 'quy' },
|
||||
azerbaijani: { IETF: 'az-AZ', 'ISO-639': 'az' },
|
||||
'bahamas creole english': { IETF: 'bah-BS', 'ISO-639': 'bah' },
|
||||
bajan: { IETF: 'bjs-BB', 'ISO-639': 'bjs' },
|
||||
balinese: { IETF: 'ban-ID', 'ISO-639': 'ban' },
|
||||
'balkan gipsy': { IETF: 'rm-RO', 'ISO-639': 'rm' },
|
||||
bambara: { IETF: 'bm-ML', 'ISO-639': 'bm' },
|
||||
banjar: { IETF: 'bjn-ID', 'ISO-639': 'bjn' },
|
||||
bashkir: { IETF: 'ba-RU', 'ISO-639': 'ba' },
|
||||
basque: { IETF: 'eu-ES', 'ISO-639': 'eu' },
|
||||
belarusian: { IETF: 'be-BY', 'ISO-639': 'be' },
|
||||
'belgian french': { IETF: 'fr-BE', 'ISO-639': 'fr' },
|
||||
bemba: { IETF: 'bem-ZM', 'ISO-639': 'bem' },
|
||||
bengali: { IETF: 'bn-IN', 'ISO-639': 'bn' },
|
||||
bhojpuri: { IETF: 'bho-IN', 'ISO-639': 'bho' },
|
||||
bihari: { IETF: 'bh-IN', 'ISO-639': 'bh' },
|
||||
bislama: { IETF: 'bi-VU', 'ISO-639': 'bi' },
|
||||
borana: { IETF: 'gax-KE', 'ISO-639': 'gax' },
|
||||
bosnian: { IETF: 'bs-BA', 'ISO-639': 'bs' },
|
||||
'bosnian (cyrillic)': { IETF: 'bs-Cyrl-BA', 'ISO-639': 'bs' },
|
||||
breton: { IETF: 'br-FR', 'ISO-639': 'br' },
|
||||
buginese: { IETF: 'bug-ID', 'ISO-639': 'bug' },
|
||||
bulgarian: { IETF: 'bg-BG', 'ISO-639': 'bg' },
|
||||
burmese: { IETF: 'my-MM', 'ISO-639': 'my' },
|
||||
catalan: { IETF: 'ca-ES', 'ISO-639': 'ca' },
|
||||
'catalan valencian': { IETF: 'cav-ES', 'ISO-639': 'cav' },
|
||||
cebuano: { IETF: 'ceb-PH', 'ISO-639': 'ceb' },
|
||||
'central atlas tamazight': { IETF: 'tzm-MA', 'ISO-639': 'tzm' },
|
||||
'central aymara': { IETF: 'ayr-BO', 'ISO-639': 'ayr' },
|
||||
'central kanuri (latin script)': { IETF: 'knc-NG', 'ISO-639': 'knc' },
|
||||
'chadian arabic': { IETF: 'shu-TD', 'ISO-639': 'shu' },
|
||||
chamorro: { IETF: 'ch-GU', 'ISO-639': 'ch' },
|
||||
cherokee: { IETF: 'chr-US', 'ISO-639': 'chr' },
|
||||
chhattisgarhi: { IETF: 'hne-IN', 'ISO-639': 'hne' },
|
||||
'chinese simplified': { IETF: 'zh-CN', 'ISO-639': 'zh' },
|
||||
'chinese trad. (hong kong)': { IETF: 'zh-HK', 'ISO-639': 'zh' },
|
||||
'chinese traditional': { IETF: 'zh-TW', 'ISO-639': 'zh' },
|
||||
'chinese traditional macau': { IETF: 'zh-MO', 'ISO-639': 'zh' },
|
||||
chittagonian: { IETF: 'ctg-BD', 'ISO-639': 'ctg' },
|
||||
chokwe: { IETF: 'cjk-AO', 'ISO-639': 'cjk' },
|
||||
'classical greek': { IETF: 'grc-GR', 'ISO-639': 'grc' },
|
||||
'comorian ngazidja': { IETF: 'zdj-KM', 'ISO-639': 'zdj' },
|
||||
coptic: { IETF: 'cop-EG', 'ISO-639': 'cop' },
|
||||
'crimean tatar': { IETF: 'crh-RU', 'ISO-639': 'crh' },
|
||||
'crioulo upper guinea': { IETF: 'pov-GW', 'ISO-639': 'pov' },
|
||||
croatian: { IETF: 'hr-HR', 'ISO-639': 'hr' },
|
||||
czech: { IETF: 'cs-CZ', 'ISO-639': 'cs' },
|
||||
danish: { IETF: 'da-DK', 'ISO-639': 'da' },
|
||||
dari: { IETF: 'prs-AF', 'ISO-639': 'prs' },
|
||||
dimli: { IETF: 'diq-TR', 'ISO-639': 'diq' },
|
||||
dutch: { IETF: 'nl-NL', 'ISO-639': 'nl' },
|
||||
dyula: { IETF: 'dyu-CI', 'ISO-639': 'dyu' },
|
||||
dzongkha: { IETF: 'dz-BT', 'ISO-639': 'dz' },
|
||||
'eastern yiddish': { IETF: 'ydd-US', 'ISO-639': 'ydd' },
|
||||
emakhuwa: { IETF: 'vmw-MZ', 'ISO-639': 'vmw' },
|
||||
english: { IETF: 'en-GB', 'ISO-639': 'en' },
|
||||
'english australia': { IETF: 'en-AU', 'ISO-639': 'en' },
|
||||
'english canada': { IETF: 'en-CA', 'ISO-639': 'en' },
|
||||
'english india': { IETF: 'en-IN', 'ISO-639': 'en' },
|
||||
'english ireland': { IETF: 'en-IE', 'ISO-639': 'en' },
|
||||
'english new zealand': { IETF: 'en-NZ', 'ISO-639': 'en' },
|
||||
'english singapore': { IETF: 'en-SG', 'ISO-639': 'en' },
|
||||
'english south africa': { IETF: 'en-ZA', 'ISO-639': 'en' },
|
||||
'english us': { IETF: 'en-US', 'ISO-639': 'en' },
|
||||
esperanto: { IETF: 'eo-EU', 'ISO-639': 'eo' },
|
||||
estonian: { IETF: 'et-EE', 'ISO-639': 'et' },
|
||||
ewe: { IETF: 'ee-GH', 'ISO-639': 'ee' },
|
||||
fanagalo: { IETF: 'fn-FNG', 'ISO-639': 'fn' },
|
||||
faroese: { IETF: 'fo-FO', 'ISO-639': 'fo' },
|
||||
fijian: { IETF: 'fj-FJ', 'ISO-639': 'fj' },
|
||||
filipino: { IETF: 'fil-PH', 'ISO-639': 'fil' },
|
||||
finnish: { IETF: 'fi-FI', 'ISO-639': 'fi' },
|
||||
flemish: { IETF: 'nl-BE', 'ISO-639': 'nl' },
|
||||
fon: { IETF: 'fon-BJ', 'ISO-639': 'fon' },
|
||||
french: { IETF: 'fr-FR', 'ISO-639': 'fr' },
|
||||
'french canada': { IETF: 'fr-CA', 'ISO-639': 'fr' },
|
||||
'french swiss': { IETF: 'fr-CH', 'ISO-639': 'fr' },
|
||||
friulian: { IETF: 'fur-IT', 'ISO-639': 'fur' },
|
||||
fula: { IETF: 'ff-FUL', 'ISO-639': 'ff' },
|
||||
galician: { IETF: 'gl-ES', 'ISO-639': 'gl' },
|
||||
gamargu: { IETF: 'mfi-NG', 'ISO-639': 'mfi' },
|
||||
garo: { IETF: 'grt-IN', 'ISO-639': 'grt' },
|
||||
georgian: { IETF: 'ka-GE', 'ISO-639': 'ka' },
|
||||
german: { IETF: 'de-DE', 'ISO-639': 'de' },
|
||||
gilbertese: { IETF: 'gil-KI', 'ISO-639': 'gil' },
|
||||
glavda: { IETF: 'glw-NG', 'ISO-639': 'glw' },
|
||||
greek: { IETF: 'el-GR', 'ISO-639': 'el' },
|
||||
'grenadian creole english': { IETF: 'gcl-GD', 'ISO-639': 'gcl' },
|
||||
guarani: { IETF: 'gn-PY', 'ISO-639': 'gn' },
|
||||
gujarati: { IETF: 'gu-IN', 'ISO-639': 'gu' },
|
||||
'guyanese creole english': { IETF: 'gyn-GY', 'ISO-639': 'gyn' },
|
||||
'haitian creole french': { IETF: 'ht-HT', 'ISO-639': 'ht' },
|
||||
'halh mongolian': { IETF: 'khk-MN', 'ISO-639': 'khk' },
|
||||
hausa: { IETF: 'ha-NE', 'ISO-639': 'ha' },
|
||||
hawaiian: { IETF: 'haw-US', 'ISO-639': 'haw' },
|
||||
hebrew: { IETF: 'he-IL', 'ISO-639': 'he' },
|
||||
higi: { IETF: 'hig-NG', 'ISO-639': 'hig' },
|
||||
hiligaynon: { IETF: 'hil-PH', 'ISO-639': 'hil' },
|
||||
'hill mari': { IETF: 'mrj-RU', 'ISO-639': 'mrj' },
|
||||
hindi: { IETF: 'hi-IN', 'ISO-639': 'hi' },
|
||||
hmong: { IETF: 'hmn-CN', 'ISO-639': 'hmn' },
|
||||
hungarian: { IETF: 'hu-HU', 'ISO-639': 'hu' },
|
||||
icelandic: { IETF: 'is-IS', 'ISO-639': 'is' },
|
||||
'igbo ibo': { IETF: 'ibo-NG', 'ISO-639': 'ibo' },
|
||||
'igbo ig': { IETF: 'ig-NG', 'ISO-639': 'ig' },
|
||||
ilocano: { IETF: 'ilo-PH', 'ISO-639': 'ilo' },
|
||||
indonesian: { IETF: 'id-ID', 'ISO-639': 'id' },
|
||||
'inuktitut greenlandic': { IETF: 'kl-GL', 'ISO-639': 'kl' },
|
||||
'irish gaelic': { IETF: 'ga-IE', 'ISO-639': 'ga' },
|
||||
italian: { IETF: 'it-IT', 'ISO-639': 'it' },
|
||||
'italian swiss': { IETF: 'it-CH', 'ISO-639': 'it' },
|
||||
'jamaican creole english': { IETF: 'jam-JM', 'ISO-639': 'jam' },
|
||||
japanese: { IETF: 'ja-JP', 'ISO-639': 'ja' },
|
||||
javanese: { IETF: 'jv-ID', 'ISO-639': 'jv' },
|
||||
jingpho: { IETF: 'kac-MM', 'ISO-639': 'kac' },
|
||||
"k'iche'": { IETF: 'quc-GT', 'ISO-639': 'quc' },
|
||||
'kabiy<69>': { IETF: 'kbp-TG', 'ISO-639': 'kbp' },
|
||||
kabuverdianu: { IETF: 'kea-CV', 'ISO-639': 'kea' },
|
||||
kabylian: { IETF: 'kab-DZ', 'ISO-639': 'kab' },
|
||||
kalenjin: { IETF: 'kln-KE', 'ISO-639': 'kln' },
|
||||
kamba: { IETF: 'kam-KE', 'ISO-639': 'kam' },
|
||||
kannada: { IETF: 'kn-IN', 'ISO-639': 'kn' },
|
||||
kanuri: { IETF: 'kr-KAU', 'ISO-639': 'kr' },
|
||||
karen: { IETF: 'kar-MM', 'ISO-639': 'kar' },
|
||||
'kashmiri (devanagari script)': { IETF: 'ks-IN', 'ISO-639': 'ks' },
|
||||
'kashmiri (arabic script)': { IETF: 'kas-IN', 'ISO-639': 'kas' },
|
||||
kazakh: { IETF: 'kk-KZ', 'ISO-639': 'kk' },
|
||||
khasi: { IETF: 'kha-IN', 'ISO-639': 'kha' },
|
||||
khmer: { IETF: 'km-KH', 'ISO-639': 'km' },
|
||||
'kikuyu kik': { IETF: 'kik-KE', 'ISO-639': 'kik' },
|
||||
'kikuyu ki': { IETF: 'ki-KE', 'ISO-639': 'ki' },
|
||||
kimbundu: { IETF: 'kmb-AO', 'ISO-639': 'kmb' },
|
||||
kinyarwanda: { IETF: 'rw-RW', 'ISO-639': 'rw' },
|
||||
kirundi: { IETF: 'rn-BI', 'ISO-639': 'rn' },
|
||||
kisii: { IETF: 'guz-KE', 'ISO-639': 'guz' },
|
||||
kongo: { IETF: 'kg-CG', 'ISO-639': 'kg' },
|
||||
konkani: { IETF: 'kok-IN', 'ISO-639': 'kok' },
|
||||
korean: { IETF: 'ko-KR', 'ISO-639': 'ko' },
|
||||
'northern kurdish': { IETF: 'kmr-TR', 'ISO-639': 'kmr' },
|
||||
'kurdish sorani': { IETF: 'ckb-IQ', 'ISO-639': 'ckb' },
|
||||
kyrgyz: { IETF: 'ky-KG', 'ISO-639': 'ky' },
|
||||
lao: { IETF: 'lo-LA', 'ISO-639': 'lo' },
|
||||
latgalian: { IETF: 'ltg-LV', 'ISO-639': 'ltg' },
|
||||
latin: { IETF: 'la-XN', 'ISO-639': 'la' },
|
||||
latvian: { IETF: 'lv-LV', 'ISO-639': 'lv' },
|
||||
ligurian: { IETF: 'lij-IT', 'ISO-639': 'lij' },
|
||||
limburgish: { IETF: 'li-NL', 'ISO-639': 'li' },
|
||||
lingala: { IETF: 'ln-LIN', 'ISO-639': 'ln' },
|
||||
lithuanian: { IETF: 'lt-LT', 'ISO-639': 'lt' },
|
||||
lombard: { IETF: 'lmo-IT', 'ISO-639': 'lmo' },
|
||||
'luba-kasai': { IETF: 'lua-CD', 'ISO-639': 'lua' },
|
||||
luganda: { IETF: 'lg-UG', 'ISO-639': 'lg' },
|
||||
luhya: { IETF: 'luy-KE', 'ISO-639': 'luy' },
|
||||
luo: { IETF: 'luo-KE', 'ISO-639': 'luo' },
|
||||
luxembourgish: { IETF: 'lb-LU', 'ISO-639': 'lb' },
|
||||
maa: { IETF: 'mas-KE', 'ISO-639': 'mas' },
|
||||
macedonian: { IETF: 'mk-MK', 'ISO-639': 'mk' },
|
||||
magahi: { IETF: 'mag-IN', 'ISO-639': 'mag' },
|
||||
maithili: { IETF: 'mai-IN', 'ISO-639': 'mai' },
|
||||
malagasy: { IETF: 'mg-MG', 'ISO-639': 'mg' },
|
||||
malay: { IETF: 'ms-MY', 'ISO-639': 'ms' },
|
||||
malayalam: { IETF: 'ml-IN', 'ISO-639': 'ml' },
|
||||
maldivian: { IETF: 'dv-MV', 'ISO-639': 'dv' },
|
||||
maltese: { IETF: 'mt-MT', 'ISO-639': 'mt' },
|
||||
mandara: { IETF: 'mfi-CM', 'ISO-639': 'mfi' },
|
||||
manipuri: { IETF: 'mni-IN', 'ISO-639': 'mni' },
|
||||
'manx gaelic': { IETF: 'gv-IM', 'ISO-639': 'gv' },
|
||||
maori: { IETF: 'mi-NZ', 'ISO-639': 'mi' },
|
||||
marathi: { IETF: 'mr-IN', 'ISO-639': 'mr' },
|
||||
margi: { IETF: 'mrt-NG', 'ISO-639': 'mrt' },
|
||||
mari: { IETF: 'mhr-RU', 'ISO-639': 'mhr' },
|
||||
marshallese: { IETF: 'mh-MH', 'ISO-639': 'mh' },
|
||||
mende: { IETF: 'men-SL', 'ISO-639': 'men' },
|
||||
meru: { IETF: 'mer-KE', 'ISO-639': 'mer' },
|
||||
mijikenda: { IETF: 'nyf-KE', 'ISO-639': 'nyf' },
|
||||
minangkabau: { IETF: 'min-ID', 'ISO-639': 'min' },
|
||||
mizo: { IETF: 'lus-IN', 'ISO-639': 'lus' },
|
||||
mongolian: { IETF: 'mn-MN', 'ISO-639': 'mn' },
|
||||
montenegrin: { IETF: 'sr-ME', 'ISO-639': 'sr' },
|
||||
morisyen: { IETF: 'mfe-MU', 'ISO-639': 'mfe' },
|
||||
'moroccan arabic': { IETF: 'ar-MA', 'ISO-639': 'ar' },
|
||||
mossi: { IETF: 'mos-BF', 'ISO-639': 'mos' },
|
||||
ndau: { IETF: 'ndc-MZ', 'ISO-639': 'ndc' },
|
||||
ndebele: { IETF: 'nr-ZA', 'ISO-639': 'nr' },
|
||||
nepali: { IETF: 'ne-NP', 'ISO-639': 'ne' },
|
||||
'nigerian fulfulde': { IETF: 'fuv-NG', 'ISO-639': 'fuv' },
|
||||
niuean: { IETF: 'niu-NU', 'ISO-639': 'niu' },
|
||||
'north azerbaijani': { IETF: 'azj-AZ', 'ISO-639': 'azj' },
|
||||
sesotho: { IETF: 'nso-ZA', 'ISO-639': 'nso' },
|
||||
'northern uzbek': { IETF: 'uzn-UZ', 'ISO-639': 'uzn' },
|
||||
'norwegian bokm<6B>l': { IETF: 'nb-NO', 'ISO-639': 'nb' },
|
||||
'norwegian nynorsk': { IETF: 'nn-NO', 'ISO-639': 'nn' },
|
||||
nuer: { IETF: 'nus-SS', 'ISO-639': 'nus' },
|
||||
nyanja: { IETF: 'ny-MW', 'ISO-639': 'ny' },
|
||||
occitan: { IETF: 'oc-FR', 'ISO-639': 'oc' },
|
||||
'occitan aran': { IETF: 'oc-ES', 'ISO-639': 'oc' },
|
||||
odia: { IETF: 'or-IN', 'ISO-639': 'or' },
|
||||
oriya: { IETF: 'ory-IN', 'ISO-639': 'ory' },
|
||||
urdu: { IETF: 'ur-PK', 'ISO-639': 'ur' },
|
||||
palauan: { IETF: 'pau-PW', 'ISO-639': 'pau' },
|
||||
pali: { IETF: 'pi-IN', 'ISO-639': 'pi' },
|
||||
pangasinan: { IETF: 'pag-PH', 'ISO-639': 'pag' },
|
||||
papiamentu: { IETF: 'pap-CW', 'ISO-639': 'pap' },
|
||||
pashto: { IETF: 'ps-PK', 'ISO-639': 'ps' },
|
||||
persian: { IETF: 'fa-IR', 'ISO-639': 'fa' },
|
||||
pijin: { IETF: 'pis-SB', 'ISO-639': 'pis' },
|
||||
'plateau malagasy': { IETF: 'plt-MG', 'ISO-639': 'plt' },
|
||||
polish: { IETF: 'pl-PL', 'ISO-639': 'pl' },
|
||||
portuguese: { IETF: 'pt-PT', 'ISO-639': 'pt' },
|
||||
'portuguese brazil': { IETF: 'pt-BR', 'ISO-639': 'pt' },
|
||||
potawatomi: { IETF: 'pot-US', 'ISO-639': 'pot' },
|
||||
punjabi: { IETF: 'pa-IN', 'ISO-639': 'pa' },
|
||||
'punjabi (pakistan)': { IETF: 'pnb-PK', 'ISO-639': 'pnb' },
|
||||
quechua: { IETF: 'qu-PE', 'ISO-639': 'qu' },
|
||||
rohingya: { IETF: 'rhg-MM', 'ISO-639': 'rhg' },
|
||||
rohingyalish: { IETF: 'rhl-MM', 'ISO-639': 'rhl' },
|
||||
romanian: { IETF: 'ro-RO', 'ISO-639': 'ro' },
|
||||
romansh: { IETF: 'roh-CH', 'ISO-639': 'roh' },
|
||||
rundi: { IETF: 'run-BI', 'ISO-639': 'run' },
|
||||
russian: { IETF: 'ru-RU', 'ISO-639': 'ru' },
|
||||
'saint lucian creole french': { IETF: 'acf-LC', 'ISO-639': 'acf' },
|
||||
samoan: { IETF: 'sm-WS', 'ISO-639': 'sm' },
|
||||
sango: { IETF: 'sg-CF', 'ISO-639': 'sg' },
|
||||
sanskrit: { IETF: 'sa-IN', 'ISO-639': 'sa' },
|
||||
santali: { IETF: 'sat-IN', 'ISO-639': 'sat' },
|
||||
sardinian: { IETF: 'sc-IT', 'ISO-639': 'sc' },
|
||||
'scots gaelic': { IETF: 'gd-GB', 'ISO-639': 'gd' },
|
||||
sena: { IETF: 'seh-ZW', 'ISO-639': 'seh' },
|
||||
'serbian cyrillic': { IETF: 'sr-Cyrl-RS', 'ISO-639': 'sr' },
|
||||
'serbian latin': { IETF: 'sr-Latn-RS', 'ISO-639': 'sr' },
|
||||
'seselwa creole french': { IETF: 'crs-SC', 'ISO-639': 'crs' },
|
||||
'setswana (south africa)': { IETF: 'tn-ZA', 'ISO-639': 'tn' },
|
||||
shan: { IETF: 'shn-MM', 'ISO-639': 'shn' },
|
||||
shona: { IETF: 'sn-ZW', 'ISO-639': 'sn' },
|
||||
sicilian: { IETF: 'scn-IT', 'ISO-639': 'scn' },
|
||||
silesian: { IETF: 'szl-PL', 'ISO-639': 'szl' },
|
||||
'sindhi snd': { IETF: 'snd-PK', 'ISO-639': 'snd' },
|
||||
'sindhi sd': { IETF: 'sd-PK', 'ISO-639': 'sd' },
|
||||
sinhala: { IETF: 'si-LK', 'ISO-639': 'si' },
|
||||
slovak: { IETF: 'sk-SK', 'ISO-639': 'sk' },
|
||||
slovenian: { IETF: 'sl-SI', 'ISO-639': 'sl' },
|
||||
somali: { IETF: 'so-SO', 'ISO-639': 'so' },
|
||||
'sotho southern': { IETF: 'st-LS', 'ISO-639': 'st' },
|
||||
'south azerbaijani': { IETF: 'azb-AZ', 'ISO-639': 'azb' },
|
||||
'southern pashto': { IETF: 'pbt-PK', 'ISO-639': 'pbt' },
|
||||
'southwestern dinka': { IETF: 'dik-SS', 'ISO-639': 'dik' },
|
||||
spanish: { IETF: 'es-ES', 'ISO-639': 'es' },
|
||||
'spanish argentina': { IETF: 'es-AR', 'ISO-639': 'es' },
|
||||
'spanish colombia': { IETF: 'es-CO', 'ISO-639': 'es' },
|
||||
'spanish latin america': { IETF: 'es-419', 'ISO-639': 'es' },
|
||||
'spanish mexico': { IETF: 'es-MX', 'ISO-639': 'es' },
|
||||
'spanish united states': { IETF: 'es-US', 'ISO-639': 'es' },
|
||||
'sranan tongo': { IETF: 'srn-SR', 'ISO-639': 'srn' },
|
||||
'standard latvian': { IETF: 'lvs-LV', 'ISO-639': 'lvs' },
|
||||
'standard malay': { IETF: 'zsm-MY', 'ISO-639': 'zsm' },
|
||||
sundanese: { IETF: 'su-ID', 'ISO-639': 'su' },
|
||||
swahili: { IETF: 'sw-KE', 'ISO-639': 'sw' },
|
||||
swati: { IETF: 'ss-SZ', 'ISO-639': 'ss' },
|
||||
swedish: { IETF: 'sv-SE', 'ISO-639': 'sv' },
|
||||
'swiss german': { IETF: 'de-CH', 'ISO-639': 'de' },
|
||||
'syriac (aramaic)': { IETF: 'syc-TR', 'ISO-639': 'syc' },
|
||||
tagalog: { IETF: 'tl-PH', 'ISO-639': 'tl' },
|
||||
tahitian: { IETF: 'ty-PF', 'ISO-639': 'ty' },
|
||||
tajik: { IETF: 'tg-TJ', 'ISO-639': 'tg' },
|
||||
'tamashek (tuareg)': { IETF: 'tmh-DZ', 'ISO-639': 'tmh' },
|
||||
tamasheq: { IETF: 'taq-ML', 'ISO-639': 'taq' },
|
||||
'tamil india': { IETF: 'ta-IN', 'ISO-639': 'ta' },
|
||||
'tamil sri lanka': { IETF: 'ta-LK', 'ISO-639': 'ta' },
|
||||
taroko: { IETF: 'trv-TW', 'ISO-639': 'trv' },
|
||||
tatar: { IETF: 'tt-RU', 'ISO-639': 'tt' },
|
||||
telugu: { IETF: 'te-IN', 'ISO-639': 'te' },
|
||||
tetum: { IETF: 'tet-TL', 'ISO-639': 'tet' },
|
||||
thai: { IETF: 'th-TH', 'ISO-639': 'th' },
|
||||
tibetan: { IETF: 'bo-CN', 'ISO-639': 'bo' },
|
||||
tigrinya: { IETF: 'ti-ET', 'ISO-639': 'ti' },
|
||||
'tok pisin': { IETF: 'tpi-PG', 'ISO-639': 'tpi' },
|
||||
tokelauan: { IETF: 'tkl-TK', 'ISO-639': 'tkl' },
|
||||
tongan: { IETF: 'to-TO', 'ISO-639': 'to' },
|
||||
'tosk albanian': { IETF: 'als-AL', 'ISO-639': 'als' },
|
||||
tsonga: { IETF: 'ts-ZA', 'ISO-639': 'ts' },
|
||||
tswa: { IETF: 'tsc-MZ', 'ISO-639': 'tsc' },
|
||||
tswana: { IETF: 'tn-BW', 'ISO-639': 'tn' },
|
||||
tumbuka: { IETF: 'tum-MW', 'ISO-639': 'tum' },
|
||||
turkish: { IETF: 'tr-TR', 'ISO-639': 'tr' },
|
||||
turkmen: { IETF: 'tk-TM', 'ISO-639': 'tk' },
|
||||
tuvaluan: { IETF: 'tvl-TV', 'ISO-639': 'tvl' },
|
||||
twi: { IETF: 'tw-GH', 'ISO-639': 'tw' },
|
||||
udmurt: { IETF: 'udm-RU', 'ISO-639': 'udm' },
|
||||
ukrainian: { IETF: 'uk-UA', 'ISO-639': 'uk' },
|
||||
uma: { IETF: 'ppk-ID', 'ISO-639': 'ppk' },
|
||||
umbundu: { IETF: 'umb-AO', 'ISO-639': 'umb' },
|
||||
'uyghur uig': { IETF: 'uig-CN', 'ISO-639': 'uig' },
|
||||
'uyghur ug': { IETF: 'ug-CN', 'ISO-639': 'ug' },
|
||||
uzbek: { IETF: 'uz-UZ', 'ISO-639': 'uz' },
|
||||
venetian: { IETF: 'vec-IT', 'ISO-639': 'vec' },
|
||||
vietnamese: { IETF: 'vi-VN', 'ISO-639': 'vi' },
|
||||
'vincentian creole english': { IETF: 'svc-VC', 'ISO-639': 'svc' },
|
||||
'virgin islands creole english': { IETF: 'vic-US', 'ISO-639': 'vic' },
|
||||
wallisian: { IETF: 'wls-WF', 'ISO-639': 'wls' },
|
||||
'waray (philippines)': { IETF: 'war-PH', 'ISO-639': 'war' },
|
||||
welsh: { IETF: 'cy-GB', 'ISO-639': 'cy' },
|
||||
'west central oromo': { IETF: 'gaz-ET', 'ISO-639': 'gaz' },
|
||||
'western persian': { IETF: 'pes-IR', 'ISO-639': 'pes' },
|
||||
wolof: { IETF: 'wo-SN', 'ISO-639': 'wo' },
|
||||
xhosa: { IETF: 'xh-ZA', 'ISO-639': 'xh' },
|
||||
yiddish: { IETF: 'yi-YD', 'ISO-639': 'yi' },
|
||||
yoruba: { IETF: 'yo-NG', 'ISO-639': 'yo' },
|
||||
zulu: { IETF: 'zu-ZA', 'ISO-639': 'zu' },
|
||||
};
|
||||
|
||||
module.exports = { languages };
|
||||
|
|
@ -73,8 +73,6 @@ fetch(path.join(__dirname, '../logs/activity.log'))
|
|||
tableBody.appendChild(row);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
// console.error('Error fetching log file:', error);
|
||||
});
|
||||
.catch((error) => {});
|
||||
|
||||
module.exports = logger;
|
||||
|
|
|
|||
49
src/js/mediaDevices.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
let micSelect = document.querySelector('#microphone');
|
||||
let selectedMic;
|
||||
|
||||
function getAvailableMediaDevices(type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((devices) => {
|
||||
const microphones = devices.filter((device) => device.kind === type);
|
||||
resolve(microphones);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Microphones
|
||||
getAvailableMediaDevices('audioinput')
|
||||
.then((microphones) => {
|
||||
let i = 0;
|
||||
let tempname = '';
|
||||
for (let mic of microphones) {
|
||||
if (mic.deviceId === 'default') {
|
||||
tempname = mic.label.slice(10); // remove "default -" from the label to get the default device name.
|
||||
}
|
||||
|
||||
if (mic.deviceId === 'communications' || mic.label === tempname) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const option = document.createElement('option');
|
||||
|
||||
// Set the options value and text.
|
||||
option.value = i;
|
||||
option.innerHTML = `${mic.label}`;
|
||||
|
||||
// Add the option to the voice selector.
|
||||
micSelect.appendChild(option);
|
||||
|
||||
if (i === microphones.length - 1) {
|
||||
document.getElementById('microphone').value = settings.STT.SELECTED_MICROPHONE;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error retrieving microphones:', error);
|
||||
});
|
||||
|
|
@ -1,22 +1,11 @@
|
|||
const twitchTemplate = `
|
||||
<div class="mmg">
|
||||
<div class="icon-container">
|
||||
<img class="user-img" src="" />
|
||||
<img class="status-circle" src="./images/twitch-icon.png" />
|
||||
</div>
|
||||
<div class="msg-box">
|
||||
<div class="flr">
|
||||
<div class="messages">
|
||||
<span class="timestamp">
|
||||
<span class="username"></span>
|
||||
<span class="post-time"></span>
|
||||
</span>
|
||||
<br>
|
||||
<p class="msg"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon-container">
|
||||
<img class="user-img" src="" />
|
||||
<img class="status-circle" src="./images/twitch-icon.png" />
|
||||
</div>
|
||||
<span class="username"></span>
|
||||
<div class="msg-box">
|
||||
</div>
|
||||
`.trim();
|
||||
|
||||
const userTemplate = `
|
||||
|
|
@ -31,7 +20,6 @@ const userTemplate = `
|
|||
<span class="username">You</span>
|
||||
<span class="post-time"></span>
|
||||
</span>
|
||||
<br>
|
||||
<p class="msg"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ const fs = require('fs');
|
|||
const ini = require('ini');
|
||||
const path = require('path'); // get directory path
|
||||
|
||||
const { ipcRenderer } = require('electron'); // necessary electron libraries to send data to the app
|
||||
const say = require('say');
|
||||
const request = require('request');
|
||||
const langdetect = require('langdetect');
|
||||
const { ipcRenderer, shell } = require('electron'); // necessary electron libraries to send data to the app
|
||||
const io = require('socket.io-client');
|
||||
|
||||
const util = require('util');
|
||||
|
|
@ -19,7 +16,8 @@ const { Socket } = require('socket.io-client');
|
|||
const main = ipcRenderer.sendSync('environment');
|
||||
|
||||
const resourcesPath = main.resourcesPath;
|
||||
const settingsPath = main.settingsPath.toString();
|
||||
let settingsPath = main.settingsPath.toString();
|
||||
let pythonPath = main.pythonPath.toString();
|
||||
const settings = main.settings;
|
||||
|
||||
// TODO: remove gooogle voices txt and use api instead
|
||||
|
|
@ -27,8 +25,6 @@ const googleVoices = fs.readFileSync(path.join(__dirname, './config/googleVoices
|
|||
// 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 languagesObject = fs.readFileSync(path.join(__dirname, './config/languages.txt')).toString().split('\r\n');
|
||||
|
||||
// html elements
|
||||
const root = document.documentElement;
|
||||
const ttsSelector = document.body.querySelector('#TTSSelector');
|
||||
|
|
@ -36,41 +32,39 @@ const amazonVoiceSelect = document.querySelector('#amazonVoice'); // obtain the
|
|||
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
|
||||
|
||||
// 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 talk = require(path.join(__dirname, './js/voiceQueue')); // Voice queue system
|
||||
const config = require(path.join(__dirname, './js/settings'));
|
||||
|
||||
const mediaDevices = require(path.join(__dirname, './js/mediaDevices'));
|
||||
|
||||
let notificationSounds = path.join(__dirname, './sounds/notifications');
|
||||
let sttModels = path.join(__dirname, '../speech_to_text_models');
|
||||
|
||||
function reset() {
|
||||
ipcRenderer.send('restart');
|
||||
}
|
||||
|
||||
let server;
|
||||
let socket;
|
||||
|
||||
function setServer() {
|
||||
if (!settings.SERVER.USE_SERVER) {
|
||||
return;
|
||||
}
|
||||
server = require(path.join(__dirname, './js/server'));
|
||||
socket = io(`http://localhost:${settings.SERVER.PORT}`); // Connect to your Socket.IO server
|
||||
}
|
||||
|
||||
setServer();
|
||||
let server = require(path.join(__dirname, './js/server'));
|
||||
const backend = require(path.join(__dirname, './js/backend'));
|
||||
let socket = io(`http://localhost:${settings.GENERAL.PORT}`); // Connect to your Socket.IO server
|
||||
|
||||
let twitch = settings.TWITCH.USE_TWITCH ? require(path.join(__dirname, './js/twitch')) : '';
|
||||
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;
|
||||
|
||||
// initialize values
|
||||
config.getGeneralSettings();
|
||||
|
|
@ -78,7 +72,6 @@ config.getGeneralSettings();
|
|||
const TTSVolume = 1;
|
||||
|
||||
const notificationSoundVolume = 1;
|
||||
// const slider = document.body.querySelector('#slider');
|
||||
const StartDateAndTime = Date.now();
|
||||
const speakButton = document.querySelector('#speakBtn');
|
||||
|
||||
|
|
@ -105,9 +98,29 @@ fs.readdir(notificationSounds, (err, files) => {
|
|||
notificationSound.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND;
|
||||
});
|
||||
|
||||
// Check for installed stt models
|
||||
fs.readdir(sttModels, (err, files) => {
|
||||
for (let 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;
|
||||
});
|
||||
|
||||
async function getAudioDevices() {
|
||||
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
|
||||
// logger.info('enumerateDevices() not supported.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -129,122 +142,36 @@ getAudioDevices();
|
|||
function setLanguagesinSelect(languageSelector, setting) {
|
||||
let languageSelect = document.querySelector(languageSelector); // obtain the html reference of the google voices comboBox
|
||||
|
||||
const languages = Object.keys(languagesObject);
|
||||
languages.forEach((language) => {
|
||||
const option = document.createElement('option');
|
||||
|
||||
option.value = language;
|
||||
option.innerHTML = languagesObject[language];
|
||||
|
||||
languageSelect.appendChild(option);
|
||||
});
|
||||
for (const language in languageObject.languages) {
|
||||
if (languageObject.languages.hasOwnProperty(language)) {
|
||||
const iso639 = languageObject.languages[language]['ISO-639'];
|
||||
const option = document.createElement('option');
|
||||
option.value = iso639;
|
||||
option.innerHTML = `${iso639} - ${language}`;
|
||||
languageSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
languageSelect.selectedIndex = setting;
|
||||
}
|
||||
|
||||
setLanguagesinSelect('#primaryLanguage', settings.TTS.PRIMARY_TTS_LANGUAGE_INDEX);
|
||||
setLanguagesinSelect('#language', settings.GENERAL.LANGUAGE);
|
||||
setLanguagesinSelect('#defaultLanguage', settings.TTS.PRIMARY_TTS_LANGUAGE_INDEX);
|
||||
setLanguagesinSelect('#secondaryLanguage', settings.TTS.SECONDARY_TTS_LANGUAGE_INDEX);
|
||||
|
||||
function getInstalledVoices(callback) {
|
||||
say.getInstalledVoices((err, voices) => {
|
||||
function setVoicesinSelect(voiceSelector) {
|
||||
let voiceSelect = document.querySelector(voiceSelector); // obtain the html reference of the google voices comboBox
|
||||
function addVoiceService(name) {
|
||||
function addToselect(select) {
|
||||
let ttsService = document.querySelector(select);
|
||||
const option = document.createElement('option');
|
||||
ttsService.appendChild(option);
|
||||
|
||||
const internalTTSHeader = document.createElement('optgroup');
|
||||
internalTTSHeader.label = 'Internal TTS';
|
||||
voiceSelect.appendChild(internalTTSHeader);
|
||||
|
||||
// const installedTTS = document.querySelector('#installedTTS'); // obtain the html reference of the installedTTS comboBox
|
||||
voices.forEach((voice, i) => {
|
||||
const option = document.createElement('option');
|
||||
|
||||
option.value = i;
|
||||
option.innerHTML = voice;
|
||||
|
||||
// installedTTS.appendChild(option);
|
||||
internalTTSHeader.appendChild(option);
|
||||
});
|
||||
}
|
||||
setVoicesinSelect('#primaryVoice');
|
||||
setVoicesinSelect('#secondaryVoice');
|
||||
|
||||
callback();
|
||||
});
|
||||
option.value = name;
|
||||
option.innerHTML = name;
|
||||
}
|
||||
addToselect('#primaryTTSService');
|
||||
addToselect('#secondaryTTSService');
|
||||
}
|
||||
|
||||
function getAmazonVoices(callback) {
|
||||
if (!settings.AMAZON.USE_AMAZON) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
function setVoicesinSelect(voiceSelector) {
|
||||
let voiceSelect = document.querySelector(voiceSelector); // obtain the html reference of the google voices comboBox
|
||||
|
||||
const internalTTSHeader = document.createElement('optgroup');
|
||||
internalTTSHeader.label = 'Amazon TTS';
|
||||
voiceSelect.appendChild(internalTTSHeader);
|
||||
|
||||
const voices = Object.keys(amazonVoices);
|
||||
voices.forEach((voice) => {
|
||||
const option = document.createElement('option');
|
||||
|
||||
option.value = voice;
|
||||
option.innerHTML = amazonVoices[voice];
|
||||
|
||||
internalTTSHeader.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
setVoicesinSelect('#primaryVoice');
|
||||
setVoicesinSelect('#secondaryVoice');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
function getGoogleVoices(callback) {
|
||||
if (!settings.GOOGLE.USE_GOOGLE) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
function setVoicesinSelect(voiceSelector) {
|
||||
let voiceSelect = document.querySelector(voiceSelector); // obtain the html reference of the google voices comboBox
|
||||
|
||||
const internalTTSHeader = document.createElement('optgroup');
|
||||
internalTTSHeader.label = 'Google TTS';
|
||||
voiceSelect.appendChild(internalTTSHeader);
|
||||
|
||||
const googleVoiceSelect = document.querySelector('#googleVoice'); // obtain the html reference of the google voices comboBox
|
||||
const voices = Object.keys(googleVoices);
|
||||
voices.forEach((voice) => {
|
||||
const option = document.createElement('option');
|
||||
option.classList.add('option');
|
||||
|
||||
option.value = voice;
|
||||
option.innerHTML = googleVoices[voice];
|
||||
|
||||
internalTTSHeader.appendChild(option);
|
||||
});
|
||||
}
|
||||
setVoicesinSelect('#primaryVoice');
|
||||
setVoicesinSelect('#secondaryVoice');
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
getGoogleVoices(function () {
|
||||
getAmazonVoices(function () {
|
||||
getInstalledVoices(function () {
|
||||
let primaryVoice = document.querySelector('#primaryVoice');
|
||||
primaryVoice.selectedIndex = settings.TTS.PRIMARY_TTS_VOICE;
|
||||
|
||||
let secondaryVoice = document.querySelector('#secondaryVoice');
|
||||
secondaryVoice.selectedIndex = settings.TTS.SECONDARY_TTS_VOICE;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Small tooltip
|
||||
Array.from(document.body.querySelectorAll('[tip]')).forEach((el) => {
|
||||
const tip = document.createElement('div');
|
||||
|
|
@ -270,6 +197,13 @@ Array.from(document.body.querySelectorAll('[tip]')).forEach((el) => {
|
|||
|
||||
function showChatMessage(article) {
|
||||
document.querySelector('#chatBox').appendChild(article);
|
||||
|
||||
const usernameHtml = article.querySelector('.username');
|
||||
var style = getComputedStyle(usernameHtml);
|
||||
var style2 = getComputedStyle(usernameHtml);
|
||||
|
||||
const msg = article.querySelector('.msg-box');
|
||||
|
||||
const messages = Array.from(document.body.querySelectorAll('.msg-container'));
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
lastMessage.scrollIntoView({ behavior: 'smooth' });
|
||||
|
|
@ -279,8 +213,9 @@ function getPostTime() {
|
|||
const date = new Date();
|
||||
document.body.querySelectorAll('.container').innerHTML = date.getHours();
|
||||
const hours = date.getHours();
|
||||
var ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
const minutes = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes();
|
||||
const time = `${hours}:${minutes}`;
|
||||
const time = `${hours}:${minutes} ${ampm}`;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
|
@ -310,20 +245,3 @@ hideText('.password-toggle-btn1', '#TWITCH_OAUTH_TOKEN');
|
|||
hideText('.password-toggle-btn4', '#AMAZON_ACCESS_KEY');
|
||||
hideText('.password-toggle-btn5', '#AMAZON_ACCESS_SECRET');
|
||||
hideText('.password-toggle-btn6', '#GOOGLE_API_KEY');
|
||||
|
||||
// Amazon TTS
|
||||
// const polly = new Polly(amazonCredentials);
|
||||
// const options = {
|
||||
// text: 'Hallo mijn naam is KEES',
|
||||
// voiceId: 'Lotte',
|
||||
// };
|
||||
|
||||
// const fileStream = fs.createWriteStream(path.join(resourcesPath, '/public/sounds/tts/Amazon_audio.mp3'));
|
||||
|
||||
// polly.textToSpeech(options, (err, audioStream) => {
|
||||
// if (err) {
|
||||
// return console.warn(err.message);
|
||||
// }
|
||||
// audioStream.pipe(fileStream);
|
||||
// return 1;
|
||||
// });
|
||||
|
|
|
|||
|
|
@ -1,21 +1,14 @@
|
|||
const express = require('express');
|
||||
const app = express();
|
||||
const path = require('path');
|
||||
const http = require('http').createServer(app);
|
||||
const io = require('socket.io')(http);
|
||||
const http = require('http');
|
||||
const localServer = http.createServer(app);
|
||||
const io = require('socket.io')(localServer);
|
||||
|
||||
if (!settings.SERVER.USE_SERVER) {
|
||||
return;
|
||||
}
|
||||
let requestCount = 0;
|
||||
|
||||
const PORT = settings.SERVER.PORT;
|
||||
|
||||
let isVtuberEnabled = true;
|
||||
let isChatBubbleEnabled = true;
|
||||
|
||||
function startVtuber() {
|
||||
if (!settings.SERVER.USE_VTUBER) {
|
||||
isVtuberEnabled = false;
|
||||
function startVtuberModule() {
|
||||
if (!settings.MODULES.USE_VTUBER) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -24,16 +17,17 @@ function startVtuber() {
|
|||
let vtuber = document.body.querySelector('#BrowsersourceVtuber');
|
||||
let vtuberframe = document.createElement('iframe');
|
||||
vtuberframe.class = 'frame';
|
||||
vtuberframe.src = `http://localhost:${PORT}/vtuber`;
|
||||
vtuberframe.src = `http://localhost:${settings.GENERAL.PORT}/vtuber`;
|
||||
vtuberframe.style.width = '100%';
|
||||
vtuberframe.style.height = '100%';
|
||||
vtuberframe.frameBorder = 0;
|
||||
vtuber.appendChild(vtuberframe);
|
||||
}
|
||||
|
||||
function startChatBubble() {
|
||||
if (!settings.SERVER.USE_CHATBUBBLE) {
|
||||
isChatBubbleEnabled = false;
|
||||
startVtuberModule();
|
||||
|
||||
function startChatBubbleModule() {
|
||||
if (!settings.MODULES.USE_CHATBUBBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -42,27 +36,34 @@ function startChatBubble() {
|
|||
let chat = document.body.querySelector('#BrowsersourceChat');
|
||||
let chatframe = document.createElement('iframe');
|
||||
chatframe.class = 'frame';
|
||||
chatframe.src = `http://localhost:${PORT}/chat`;
|
||||
chatframe.src = `http://localhost:${settings.GENERAL.PORT}/chat`;
|
||||
chatframe.style.width = '100%';
|
||||
chatframe.style.height = '100%';
|
||||
chatframe.frameBorder = 0;
|
||||
chat.appendChild(chatframe);
|
||||
}
|
||||
|
||||
startChatBubbleModule();
|
||||
|
||||
function startSTT() {}
|
||||
|
||||
// Middleware to conditionally serve routes
|
||||
app.use((req, res, next) => {
|
||||
if (!isVtuberEnabled && req.path === '/vtuber') {
|
||||
if (!settings.MODULES.USE_VTUBER && req.path === '/vtuber') {
|
||||
res.sendStatus(404); // Return a 404 status for /vtuber when it's disabled
|
||||
} else if (!isChatBubbleEnabled && req.path === '/chat') {
|
||||
} else if (!settings.MODULES.USE_CHATBUBBLE && req.path === '/chat') {
|
||||
res.sendStatus(404); // Return a 404 status for /chat when it's disabled
|
||||
} else {
|
||||
next(); // Proceed to the next middleware or route handler
|
||||
}
|
||||
});
|
||||
|
||||
http.listen(PORT, () => {
|
||||
startVtuber();
|
||||
startChatBubble();
|
||||
localServer.listen(settings.GENERAL.PORT, () => {
|
||||
startVtuberModule();
|
||||
startChatBubbleModule();
|
||||
|
||||
if (settings.TTS.USE_TTS) {
|
||||
}
|
||||
});
|
||||
|
||||
// Handle socket connections
|
||||
|
|
@ -78,4 +79,4 @@ io.on('connection', (socket) => {
|
|||
socket.on('disconnect', () => {});
|
||||
});
|
||||
|
||||
module.exports = { startVtuber, startChatBubble };
|
||||
module.exports = { startVtuberModule, startChatBubbleModule };
|
||||
|
|
|
|||
|
|
@ -1,23 +1,110 @@
|
|||
function getGeneralSettings() {
|
||||
// General
|
||||
document.body.querySelector('#PORT').value = settings.GENERAL.PORT;
|
||||
|
||||
// Theme
|
||||
document.querySelector('#USE_CUSTOM_THEME').value = settings.THEME.USE_CUSTOM_THEME;
|
||||
document.body.querySelector('#USE_CUSTOM_THEME').checked = settings.THEME.USE_CUSTOM_THEME === true ? 1 : 0;
|
||||
theme.setTheme();
|
||||
|
||||
// STT
|
||||
document.body.querySelector('#USE_STT').checked = settings.STT.USE_STT;
|
||||
|
||||
// Language detection
|
||||
document.body.querySelector('#USE_DETECTION').checked = settings.LANGUAGE.USE_DETECTION;
|
||||
|
||||
// TTS
|
||||
document.body.querySelector('#USE_TTS').checked = settings.TTS.USE_TTS;
|
||||
|
||||
// Notification sounds
|
||||
document.body.querySelector('#USE_NOTIFICATION_SOUNDS').checked = settings.AUDIO.USE_NOTIFICATION_SOUNDS;
|
||||
|
||||
// Twitch
|
||||
document.body.querySelector('#USE_TWITCH').checked = settings.TWITCH.USE_TWITCH;
|
||||
document.body.querySelector('#TWITCH_CHANNEL_NAME').value = settings.TWITCH.CHANNEL_NAME;
|
||||
document.body.querySelector('#TWITCH_OAUTH_TOKEN').value = settings.TWITCH.OAUTH_TOKEN;
|
||||
|
||||
// Modules
|
||||
document.body.querySelector('#USE_MODULES').checked = settings.MODULES.USE_MODULES;
|
||||
document.body.querySelector('#USE_VTUBER').checked = settings.MODULES.USE_VTUBER;
|
||||
document.body.querySelector('#VTUBER_URL').value = `http://localhost:${settings.GENERAL.PORT}/vtuber/`;
|
||||
showMenuButton('#btnBrowsersourceVtuber', settings.MODULES.USE_VTUBER);
|
||||
document.body.querySelector('#USE_CHATBUBBLE').checked = settings.GENERAL.USE_CHATBUBBLE;
|
||||
document.body.querySelector('#CHATBUBBLE_URL').value = `http://localhost:${settings.GENERAL.PORT}/chat/`;
|
||||
showMenuButton('#btnBrowsersourceChat', settings.GENERAL.USE_CHATBUBBLE);
|
||||
|
||||
// Amazon
|
||||
document.body.querySelector('#USE_AMAZON').checked = settings.AMAZON.USE_AMAZON;
|
||||
document.body.querySelector('#AMAZON_ACCESS_KEY').value = settings.AMAZON.ACCESS_KEY;
|
||||
document.body.querySelector('#AMAZON_ACCESS_SECRET').value = settings.AMAZON.ACCESS_SECRET;
|
||||
|
||||
// Google
|
||||
document.body.querySelector('#USE_GOOGLE').checked = settings.GOOGLE.USE_GOOGLE;
|
||||
document.body.querySelector('#GOOGLE_API_KEY').value = settings.GOOGLE.API_KEY;
|
||||
}
|
||||
|
||||
document.body.querySelector('#primaryAmazonVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#primaryAmazonVoice');
|
||||
settings.AMAZON.PRIMARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved Amazon primary voice!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#secondaryAmazonVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#secondaryAmazonVoice');
|
||||
settings.AMAZON.SECONDARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved Amazon secondary voice!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#primaryGoogleVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#primaryGoogleVoice');
|
||||
settings.GOOGLE.PRIMARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved Google primary voice!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#secondaryGoogleVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#secondaryGoogleVoice');
|
||||
settings.GOOGLE.SECONDARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved Google secondary voice!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#primaryVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#primaryVoice');
|
||||
settings.TTS.PRIMARY_TTS_VOICE = select.selectedIndex;
|
||||
settings.TTS.PRIMARY_TTS_NAME = select.options[select.selectedIndex].text;
|
||||
settings.TTS.PRIMARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved primary voice!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#primaryLanguage').addEventListener('change', () => {
|
||||
var select = document.querySelector('#primaryLanguage');
|
||||
document.body.querySelector('#microphone').addEventListener('change', () => {
|
||||
var select = document.querySelector('#microphone');
|
||||
settings.STT.MICROPHONE = select.value;
|
||||
settings.STT.MICROPHONE_ID = select.options[select.selectedIndex].text;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved microphone!', 'success');
|
||||
startVoiceRecognition();
|
||||
});
|
||||
|
||||
document.body.querySelector('#sttModel').addEventListener('change', () => {
|
||||
var select = document.querySelector('#sttModel');
|
||||
settings.STT.LANGUAGE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved voice detection language!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#defaultLanguage').addEventListener('change', () => {
|
||||
var select = document.querySelector('#defaultLanguage');
|
||||
settings.TTS.PRIMARY_TTS_LANGUAGE_INDEX = select.selectedIndex;
|
||||
settings.TTS.PRIMARY_TTS_LANGUAGE = select.options[select.selectedIndex].text;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved primary language!', 'success');
|
||||
createNotification('Saved default language!', 'success');
|
||||
});
|
||||
|
||||
document.body.querySelector('#secondaryVoice').addEventListener('change', () => {
|
||||
var select = document.querySelector('#secondaryVoice');
|
||||
settings.TTS.SECONDARY_TTS_VOICE = select.selectedIndex;
|
||||
settings.TTS.SECONDARY_TTS_NAME = select.options[select.selectedIndex].text;
|
||||
settings.TTS.SECONDARY_VOICE = select.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved secondary voice!', 'success');
|
||||
});
|
||||
|
|
@ -57,7 +144,7 @@ document.body.querySelector('#TWITCH_OAUTH_TOKEN').addEventListener('change', ()
|
|||
});
|
||||
|
||||
document.body.querySelector('#PORT').addEventListener('change', () => {
|
||||
settings.SERVER.PORT = document.body.querySelector('#PORT').value;
|
||||
settings.GENERAL.PORT = document.body.querySelector('#PORT').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved port, please restart the application to reset the port', 'warning');
|
||||
});
|
||||
|
|
@ -86,43 +173,6 @@ document.body.querySelector('#notification').addEventListener('change', () => {
|
|||
createNotification('Saved notification sound!', 'success');
|
||||
});
|
||||
|
||||
function getGeneralSettings() {
|
||||
// Theme
|
||||
document.querySelector('#USE_CUSTOM_THEME').value = settings.THEME.USE_CUSTOM_THEME;
|
||||
document.body.querySelector('#USE_CUSTOM_THEME').checked = settings.THEME.USE_CUSTOM_THEME === true ? 1 : 0;
|
||||
theme.setTheme();
|
||||
|
||||
// TTS
|
||||
document.body.querySelector('#USE_TTS').checked = settings.TTS.USE_TTS;
|
||||
|
||||
// Notification sounds
|
||||
document.body.querySelector('#USE_NOTIFICATION_SOUNDS').checked = settings.AUDIO.USE_NOTIFICATION_SOUNDS;
|
||||
|
||||
// Twitch
|
||||
document.body.querySelector('#USE_TWITCH').checked = settings.TWITCH.USE_TWITCH;
|
||||
document.body.querySelector('#TWITCH_CHANNEL_NAME').value = settings.TWITCH.CHANNEL_NAME;
|
||||
document.body.querySelector('#TWITCH_OAUTH_TOKEN').value = settings.TWITCH.OAUTH_TOKEN;
|
||||
|
||||
// Server
|
||||
document.body.querySelector('#USE_SERVER').checked = settings.SERVER.USE_SERVER;
|
||||
document.body.querySelector('#PORT').value = settings.SERVER.PORT;
|
||||
document.body.querySelector('#USE_VTUBER').checked = settings.SERVER.USE_VTUBER;
|
||||
document.body.querySelector('#VTUBER_URL').value = `http://localhost:${settings.SERVER.PORT}/vtuber/`;
|
||||
showMenuButton('#btnBrowsersourceVtuber', settings.SERVER.USE_VTUBER);
|
||||
document.body.querySelector('#USE_CHATBUBBLE').checked = settings.SERVER.USE_CHATBUBBLE;
|
||||
document.body.querySelector('#CHATBUBBLE_URL').value = `http://localhost:${settings.SERVER.PORT}/chat/`;
|
||||
showMenuButton('#btnBrowsersourceChat', settings.SERVER.USE_CHATBUBBLE);
|
||||
|
||||
// Amazon
|
||||
document.body.querySelector('#USE_AMAZON').checked = settings.AMAZON.USE_AMAZON;
|
||||
document.body.querySelector('#AMAZON_ACCESS_KEY').value = settings.AMAZON.ACCESS_KEY;
|
||||
document.body.querySelector('#AMAZON_ACCESS_SECRET').value = settings.AMAZON.ACCESS_SECRET;
|
||||
|
||||
// Google
|
||||
document.body.querySelector('#USE_GOOGLE').checked = settings.GOOGLE.USE_GOOGLE;
|
||||
document.body.querySelector('#GOOGLE_API_KEY').value = settings.GOOGLE.API_KEY;
|
||||
}
|
||||
|
||||
function showMenuButton(menuButton, toggle) {
|
||||
let option = document.body.querySelector(menuButton);
|
||||
if (!toggle) {
|
||||
|
|
@ -199,31 +249,64 @@ document.body.querySelector('#min-button').addEventListener('click', () => {
|
|||
});
|
||||
|
||||
// #region Top bar buttons
|
||||
document.body.querySelector('#Info_USERNAME').addEventListener('click', () => {
|
||||
const key = ipcRenderer.sendSync('twitch');
|
||||
|
||||
document.body.querySelector('#Info_USERNAME').addEventListener('click', async () => {
|
||||
let element = document.body.querySelector('#TWITCH_OAUTH_TOKEN');
|
||||
element.value = key;
|
||||
element.value = await auth.getTwitchOauthToken();
|
||||
|
||||
settings.TWITCH.OAUTH_TOKEN = key;
|
||||
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
createNotification('Saved OAuth token!', 'success');
|
||||
});
|
||||
|
||||
let hideInputToggleButton = document.body.querySelectorAll('.password-toggle-btn .password-toggle-icon .fa-eye-slash');
|
||||
hideInputToggleButton.forEach((item) => {
|
||||
item.addEventListener('click', () => {
|
||||
if (item.classList.contains('fa-eye')) {
|
||||
item.classList.remove('fa-eye');
|
||||
item.classList.add('fa-eye-slash');
|
||||
} else {
|
||||
item.classList.remove('fa-eye-slash');
|
||||
item.classList.add('fa-eye');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function hideOrShowViewerPanel() {
|
||||
const menu = document.body.querySelector('.sidepanel-right');
|
||||
const leftCircle = document.body.querySelector('.circle-right');
|
||||
|
||||
if (!settings.GENERAL.VIEWERS_PANEL) {
|
||||
menu.classList.add('collapse-menu-right');
|
||||
leftCircle.classList.add('collapse-circle-right');
|
||||
} else {
|
||||
menu.classList.remove('collapse-menu-right');
|
||||
leftCircle.classList.remove('collapse-circle-right');
|
||||
}
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
}
|
||||
|
||||
hideOrShowViewerPanel();
|
||||
|
||||
document.body.querySelector('#VIEWERS_PANEL').addEventListener('click', () => {
|
||||
if (settings.GENERAL.VIEWERS_PANEL) {
|
||||
settings.GENERAL.VIEWERS_PANEL = false;
|
||||
} else {
|
||||
settings.GENERAL.VIEWERS_PANEL = true;
|
||||
}
|
||||
hideOrShowViewerPanel();
|
||||
});
|
||||
|
||||
document.body.querySelector('#Info_VTUBER').addEventListener('click', () => {
|
||||
ipcRenderer.send('vtuber');
|
||||
shell.openExternal(`http://localhost:${settings.GENERAL.PORT}/vtuber/`);
|
||||
});
|
||||
|
||||
document.body.querySelector('#Info_CHATBUBBLE').addEventListener('click', () => {
|
||||
ipcRenderer.send('chatBubble');
|
||||
shell.openExternal(`http://localhost:${settings.GENERAL.PORT}/chat/`);
|
||||
});
|
||||
|
||||
document.body.querySelector('#max-button').addEventListener('click', () => {
|
||||
ipcRenderer.send('maximize-window');
|
||||
});
|
||||
|
||||
document.body.querySelector('#close-button').addEventListener('click', (event) => {
|
||||
document.body.querySelector('#close-button').addEventListener('click', () => {
|
||||
ipcRenderer.send('close-window');
|
||||
});
|
||||
|
||||
|
|
@ -292,45 +375,46 @@ document.body.querySelector('#USE_AMAZON').addEventListener('click', () => {
|
|||
});
|
||||
|
||||
function toggleServer() {
|
||||
const toggle = settings.SERVER.USE_SERVER;
|
||||
const toggle = settings.MODULES.USE_MODULES;
|
||||
const inputs = document.getElementsByClassName('inputServer');
|
||||
toggleRadio(toggle, inputs);
|
||||
}
|
||||
|
||||
toggleServer();
|
||||
|
||||
document.body.querySelector('#USE_SERVER').addEventListener('click', () => {
|
||||
const toggle = document.getElementById('USE_SERVER').checked;
|
||||
settings.SERVER.USE_SERVER = toggle;
|
||||
document.body.querySelector('#USE_MODULES').addEventListener('click', () => {
|
||||
const toggle = document.getElementById('USE_MODULES').checked;
|
||||
settings.MODULES.USE_MODULES = toggle;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
const inputs = document.getElementsByClassName('inputServer');
|
||||
toggleRadio(toggle, inputs);
|
||||
setServer();
|
||||
createNotification(
|
||||
`${toggle ? 'Enabled' : 'Disabled'} server settings!, the service will stop working after restarting the application`,
|
||||
`${toggle ? 'Enabled' : 'Disabled'} server settings!, the service will stop working after restarting the application
|
||||
${toggle ? '' : ', the service will stop working after restarting the application'}`,
|
||||
'success',
|
||||
);
|
||||
});
|
||||
|
||||
document.body.querySelector('#USE_VTUBER').addEventListener('change', () => {
|
||||
const toggle = document.getElementById('USE_VTUBER').checked;
|
||||
settings.SERVER.USE_VTUBER = toggle;
|
||||
settings.MODULES.USE_VTUBER = toggle;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
showMenuButton('#btnBrowsersourceVtuber', toggle);
|
||||
createNotification(
|
||||
`${toggle ? 'Enabled' : 'Disabled'} Vtuber setting!, the service will stop working after restarting the application`,
|
||||
`${toggle ? 'Enabled' : 'Disabled'} Vtuber setting!
|
||||
${toggle ? '' : ', the service will stop working after restarting the application'}`,
|
||||
'success',
|
||||
);
|
||||
server.startVtuber();
|
||||
server.startVtuberModule();
|
||||
});
|
||||
|
||||
document.body.querySelector('#USE_CHATBUBBLE').addEventListener('change', () => {
|
||||
const toggle = document.getElementById('USE_CHATBUBBLE').checked;
|
||||
settings.SERVER.USE_CHATBUBBLE = toggle;
|
||||
settings.MODULES.USE_CHATBUBBLE = toggle;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
showMenuButton('#btnBrowsersourceChat', toggle);
|
||||
createNotification(`${toggle ? 'Enabled' : 'Disabled'} chatbubble setting!`, 'success');
|
||||
server.startChatBubble();
|
||||
server.startChatBubbleModule();
|
||||
});
|
||||
|
||||
function toggleTTS() {
|
||||
|
|
@ -350,6 +434,40 @@ document.body.querySelector('#USE_TTS').addEventListener('change', () => {
|
|||
createNotification(`${toggle ? 'Enabled' : 'Disabled'} text to speech!`, 'success');
|
||||
});
|
||||
|
||||
function toggleSTT() {
|
||||
const toggle = settings.STT.USE_STT;
|
||||
const inputs = document.getElementsByClassName('inputSTT');
|
||||
toggleRadio(toggle, inputs);
|
||||
}
|
||||
|
||||
toggleSTT();
|
||||
|
||||
document.body.querySelector('#USE_STT').addEventListener('change', () => {
|
||||
const toggle = document.getElementById('USE_STT').checked;
|
||||
settings.STT.USE_STT = toggle;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
const inputs = document.getElementsByClassName('inputSTT');
|
||||
toggleRadio(toggle, inputs);
|
||||
createNotification(`${toggle ? 'Enabled' : 'Disabled'} speech to text!`, 'success');
|
||||
});
|
||||
|
||||
function toggleLanguageDetection() {
|
||||
const toggle = settings.LANGUAGE.USE_DETECTION;
|
||||
const inputs = document.getElementsByClassName('languageDetectionInput');
|
||||
toggleRadio(toggle, inputs);
|
||||
}
|
||||
|
||||
toggleLanguageDetection();
|
||||
|
||||
document.body.querySelector('#USE_DETECTION').addEventListener('change', () => {
|
||||
const toggle = document.getElementById('USE_DETECTION').checked;
|
||||
settings.LANGUAGE.USE_DETECTION = toggle;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
const inputs = document.getElementsByClassName('languageDetectionInput');
|
||||
toggleRadio(toggle, inputs);
|
||||
createNotification(`${toggle ? 'Enabled' : 'Disabled'} Language detection!`, 'success');
|
||||
});
|
||||
|
||||
function toggleNotificationSounds() {
|
||||
const toggle = settings.AUDIO.USE_NOTIFICATION_SOUNDS;
|
||||
const inputs = document.getElementsByClassName('inputNotificationSound');
|
||||
|
|
@ -407,12 +525,12 @@ if (settings.AUDIO.NOTIFICATION_VOLUME) {
|
|||
|
||||
document.body.querySelector('#ttsVolume').addEventListener('change', () => {
|
||||
let element = document.body.querySelector('#ttsVolume');
|
||||
settings.TTS.TTS_VOLUME = element.value;
|
||||
settings.AUDIO.TTS_VOLUME = element.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
|
||||
const slider = document.querySelector('#ttsVolumeSlider');
|
||||
slider.value = settings.TTS.TTS_VOLUME;
|
||||
slider.style.setProperty('--tiempotemporal', settings.TTS.TTS_VOLUME);
|
||||
slider.value = settings.AUDIO.TTS_VOLUME;
|
||||
slider.style.setProperty('--tiempotemporal', settings.AUDIO.TTS_VOLUME);
|
||||
|
||||
createNotification('Saved TTS volume!', 'success');
|
||||
});
|
||||
|
|
@ -427,7 +545,7 @@ document.body.querySelector('#ttsVolumeSlider').addEventListener('change', () =>
|
|||
e.addEventListener('input', () => {
|
||||
e.style.setProperty('--tiempotemporal', e.value);
|
||||
document.querySelector('#ttsVolume').value = e.value;
|
||||
settings.TTS.TTS_VOLUME = e.value;
|
||||
settings.AUDIO.TTS_VOLUME = e.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
});
|
||||
});
|
||||
|
|
@ -436,8 +554,8 @@ document.body.querySelector('#ttsVolumeSlider').addEventListener('mouseup', () =
|
|||
createNotification('Saved TTS volume!', 'success');
|
||||
});
|
||||
|
||||
if (settings.TTS.TTS_VOLUME) {
|
||||
document.querySelector('#ttsVolumeSlider').value = settings.TTS.TTS_VOLUME;
|
||||
if (settings.AUDIO.TTS_VOLUME) {
|
||||
document.querySelector('#ttsVolumeSlider').value = settings.AUDIO.TTS_VOLUME;
|
||||
document.querySelector('#ttsVolumeSlider').dispatchEvent(new Event('change'));
|
||||
} else {
|
||||
document.querySelector('#ttsVolumeSlider').dispatchEvent(new Event('change', { value: 50 }));
|
||||
|
|
@ -445,48 +563,38 @@ if (settings.TTS.TTS_VOLUME) {
|
|||
|
||||
document.body.querySelector('#ttsVolume').addEventListener('change', () => {
|
||||
let element = document.body.querySelector('#ttsVolume');
|
||||
settings.TTS.TTS_VOLUME = element.value;
|
||||
settings.AUDIO.TTS_VOLUME = element.value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
|
||||
const slider = document.querySelector('#ttsVolumeSlider');
|
||||
slider.value = settings.TTS.TTS_VOLUME;
|
||||
slider.style.setProperty('--tiempotemporal', settings.TTS.TTS_VOLUME);
|
||||
slider.value = settings.AUDIO.TTS_VOLUME;
|
||||
slider.style.setProperty('--tiempotemporal', settings.AUDIO.TTS_VOLUME);
|
||||
});
|
||||
|
||||
document.body.querySelector('.language-selector').addEventListener('click', () => {
|
||||
var dropdown = document.body.querySelector('.language-dropdown');
|
||||
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
|
||||
document.body.querySelector('#TestDefaultTTSButton').addEventListener('click', async () => {
|
||||
const text = document.getElementById('testPrimaryTTS').value;
|
||||
const requestData = {
|
||||
message: `user: ${text}`,
|
||||
voice: settings.TTS.PRIMARY_VOICE,
|
||||
};
|
||||
let count = await backend.getInternalTTSAudio(requestData);
|
||||
let textObject = { filtered: text, formatted: text };
|
||||
sound.playAudio({ service: 'Internal', message: textObject, count });
|
||||
});
|
||||
|
||||
document.body.querySelector('.language-dropdown').addEventListener('mouseleave', () => {
|
||||
hideDropdown();
|
||||
document.body.querySelector('#TestSecondaryTTSButton').addEventListener('click', async () => {
|
||||
const text = document.getElementById('testSecondaryTTS').value;
|
||||
const requestData = {
|
||||
message: `user: ${text}`,
|
||||
voice: settings.TTS.SECONDARY_VOICE,
|
||||
};
|
||||
|
||||
let count = await backend.getInternalTTSAudio(requestData);
|
||||
let textObject = { filtered: text, formatted: text };
|
||||
|
||||
sound.playAudio({ service: 'Internal', message: textObject, count });
|
||||
});
|
||||
|
||||
let languageSelector = document.querySelectorAll('.language-item');
|
||||
languageSelector.forEach((item) => {
|
||||
item.addEventListener('click', (event) => {
|
||||
const el = event.target;
|
||||
// tip.innerText = el.getAttribute('language');
|
||||
document.getElementById('selected-language').innerText = el.getAttribute('language');
|
||||
document.getElementById('selected-flag').innerText = el.getAttribute('flag');
|
||||
hideDropdown();
|
||||
});
|
||||
});
|
||||
|
||||
function hideDropdown() {
|
||||
var dropdown = document.body.querySelector('.language-dropdown');
|
||||
dropdown.style.display = 'none';
|
||||
}
|
||||
|
||||
// let primaryTTSSelector = document.body.querySelector(".optgroup");
|
||||
// primaryTTSSelector.forEach(item => {
|
||||
// item.addEventListener('hover', (event) => {
|
||||
// console.log(event);
|
||||
// // const optionsElement = document.getElementById(optgroupID);
|
||||
// // optionsElement.style.display = optionsElement.style.display === "none" ? "block" : "none";
|
||||
// });
|
||||
// });
|
||||
|
||||
module.exports = {
|
||||
getGeneralSettings,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,15 +3,22 @@ let currentLogoUrl = '';
|
|||
let currentUsername = '';
|
||||
let voiceSoundArray = [];
|
||||
let status = 0;
|
||||
let counter = 0;
|
||||
|
||||
const playTTS = (ttsData) =>
|
||||
const playTTS = (data) =>
|
||||
new Promise((resolve) => {
|
||||
const tts = new Audio(ttsData.path);
|
||||
ttsAudioFile = path.join(resourcesPath, `./sounds/tts/${data.service}_${data.count}.mp3`);
|
||||
const tts = new Audio(ttsAudioFile);
|
||||
console.log(settings.AUDIO.TTS_AUDIO_DEVICE);
|
||||
tts.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE);
|
||||
|
||||
tts.addEventListener('ended', () => {
|
||||
fs.unlink(ttsData.path, (err) => {
|
||||
console.log('ended');
|
||||
fs.unlink(ttsAudioFile, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
console.error('TEST');
|
||||
|
||||
resolve('finished');
|
||||
return;
|
||||
}
|
||||
resolve('finished');
|
||||
|
|
@ -20,15 +27,15 @@ const playTTS = (ttsData) =>
|
|||
|
||||
tts.setSinkId(settings.AUDIO.TTS_AUDIO_DEVICE)
|
||||
.then(() => {
|
||||
tts.volume = settings.TTS.TTS_VOLUME / 100;
|
||||
tts.play();
|
||||
|
||||
if (settings.SERVER.USE_SERVER) {
|
||||
socket.emit('xxx', currentLogoUrl, currentUsername, ttsData.message);
|
||||
}
|
||||
console.log('playing');
|
||||
tts.volume = settings.AUDIO.TTS_VOLUME / 100;
|
||||
tts.play().catch((error) => {
|
||||
resolve('finished');
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to set audio output device:', error);
|
||||
resolve('finished');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -40,8 +47,8 @@ async function shiftVoice() {
|
|||
status = 0;
|
||||
}
|
||||
|
||||
function add(ttsData) {
|
||||
voiceSoundArray.push(ttsData);
|
||||
function add(data) {
|
||||
voiceSoundArray.push(data);
|
||||
if (status === 0) {
|
||||
shiftVoice();
|
||||
}
|
||||
|
|
@ -59,37 +66,55 @@ function playNotificationSound() {
|
|||
|
||||
// Play sound function
|
||||
function playAudio(data) {
|
||||
if (settings.TTS.USE_TTS) {
|
||||
if (data.service !== '') {
|
||||
add(data);
|
||||
} else if (settings.SERVER.USE_SERVER && settings.SERVER.USE_CHATBUBBLE) {
|
||||
socket.emit('xxx', currentLogoUrl, currentUsername, data);
|
||||
}
|
||||
}
|
||||
|
||||
function playVoice(filteredMessage, logoUrl, username, message) {
|
||||
async function playVoice(filteredMessage, logoUrl, username, message) {
|
||||
trueMessage = filteredMessage;
|
||||
currentLogoUrl = logoUrl;
|
||||
currentUsername = username;
|
||||
let textObject = { filtered: filteredMessage, formatted: message };
|
||||
let voice;
|
||||
const language = langdetect.detect(filteredMessage);
|
||||
textObject.filtered = `${username}: ${filteredMessage}`;
|
||||
|
||||
if (
|
||||
settings.TTS.PRIMARY_TTS_LANGUAGE.toLowerCase() !== settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase() ||
|
||||
language[0].lang === settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase()
|
||||
) {
|
||||
voice = settings.TTS.SECONDARY_TTS_NAME;
|
||||
textObject.filtered = `${username}: ${filteredMessage}`;
|
||||
} else {
|
||||
voice = settings.TTS.PRIMARY_TTS_NAME;
|
||||
textObject.filtered = `${username}: ${filteredMessage}`;
|
||||
// if (
|
||||
// settings.TTS.PRIMARY_TTS_LANGUAGE.toLowerCase() !== settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase() &&
|
||||
// language[0].lang === settings.TTS.SECONDARY_TTS_LANGUAGE.toLowerCase()
|
||||
// ) {
|
||||
// voice = settings.TTS.SECONDARY_TTS_NAME;
|
||||
// textObject.filtered = `${username}: ${filteredMessage}`;
|
||||
// } else {
|
||||
// voice = settings.TTS.PRIMARY_TTS_NAME;
|
||||
// textObject.filtered = `${username}: ${filteredMessage}`;
|
||||
// }
|
||||
|
||||
const service = document.getElementById('primaryTTSService').value;
|
||||
|
||||
switch (service) {
|
||||
case 'Internal':
|
||||
const requestData = {
|
||||
message: textObject.filtered,
|
||||
voice: settings.TTS.PRIMARY_VOICE,
|
||||
};
|
||||
|
||||
let count = await backend.getInternalTTSAudio(requestData);
|
||||
playAudio({ service, message: textObject, count });
|
||||
break;
|
||||
case 'Amazon':
|
||||
// playAudio({ service: 'Amazon', message: textObject, count });
|
||||
break;
|
||||
case 'Google':
|
||||
// playAudio({ service: 'Google', message: textObject, count });
|
||||
break;
|
||||
}
|
||||
|
||||
if (settings.TTS.USE_TTS) {
|
||||
talk.add(textObject, voice);
|
||||
} else {
|
||||
playNotificationSound();
|
||||
if (settings.MODULES.USE_CHATBUBBLE) {
|
||||
socket.emit('xxx', currentLogoUrl, currentUsername, textObject);
|
||||
}
|
||||
|
||||
playNotificationSound();
|
||||
}
|
||||
|
||||
module.exports = { playAudio, playVoice, playNotificationSound };
|
||||
|
|
|
|||
150
src/js/theme.js
|
|
@ -1,137 +1,145 @@
|
|||
function changeColor(section, setting, tempSection) {
|
||||
document.querySelector(section).value = setting;
|
||||
const value = document.querySelector(section).value;
|
||||
root.style.setProperty(tempSection, value);
|
||||
document.querySelector(section).value = setting;
|
||||
const value = document.querySelector(section).value;
|
||||
root.style.setProperty(tempSection, value);
|
||||
}
|
||||
|
||||
function setCurrentTheme(adjustTemp = false) {
|
||||
changeColor("#MAIN_COLOR_1", settings.THEME.MAIN_COLOR_1, adjustTemp ? "--main-color1-temp" : "--main-color1");
|
||||
changeColor("#MAIN_COLOR_2", settings.THEME.MAIN_COLOR_2, adjustTemp ? "--main-color2-temp" : "--main-color2");
|
||||
changeColor("#MAIN_COLOR_3", settings.THEME.MAIN_COLOR_3, adjustTemp ? "--main-color3-temp" : "--main-color3");
|
||||
changeColor("#MAIN_COLOR_4", settings.THEME.MAIN_COLOR_4, adjustTemp ? "--main-color4-temp" : "--main-color4");
|
||||
changeColor("#TOP_BAR", settings.THEME.TOP_BAR, adjustTemp ? "--top-bar-temp" : "--top-bar");
|
||||
changeColor("#MID_SECTION", settings.THEME.MID_SECTION, adjustTemp ? "--mid-section-temp" : "--mid-section");
|
||||
changeColor("#CHAT_BUBBLE_BG", settings.THEME.CHAT_BUBBLE_BG, adjustTemp ? "--chat-bubble-temp" : "--chat-bubble");
|
||||
changeColor("#CHAT_BUBBLE_HEADER", settings.THEME.CHAT_BUBBLE_HEADER, adjustTemp ? "--chat-bubble-header-temp" : "--chat-bubble-header");
|
||||
changeColor("#CHAT_BUBBLE_MESSAGE", settings.THEME.CHAT_BUBBLE_MESSAGE, adjustTemp ? "--chat-bubble-message-temp" : "--chat-bubble-message");
|
||||
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, adjustTemp ? '--main-color1-temp' : '--main-color1');
|
||||
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, adjustTemp ? '--main-color2-temp' : '--main-color2');
|
||||
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, adjustTemp ? '--main-color3-temp' : '--main-color3');
|
||||
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, adjustTemp ? '--main-color4-temp' : '--main-color4');
|
||||
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, adjustTemp ? '--top-bar-temp' : '--top-bar');
|
||||
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, adjustTemp ? '--mid-section-temp' : '--mid-section');
|
||||
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, adjustTemp ? '--chat-bubble-temp' : '--chat-bubble');
|
||||
changeColor(
|
||||
'#CHAT_BUBBLE_HEADER',
|
||||
settings.THEME.CHAT_BUBBLE_HEADER,
|
||||
adjustTemp ? '--chat-bubble-header-temp' : '--chat-bubble-header',
|
||||
);
|
||||
changeColor(
|
||||
'#CHAT_BUBBLE_MESSAGE',
|
||||
settings.THEME.CHAT_BUBBLE_MESSAGE,
|
||||
adjustTemp ? '--chat-bubble-message-temp' : '--chat-bubble-message',
|
||||
);
|
||||
}
|
||||
|
||||
setCurrentTheme(true);
|
||||
|
||||
function setTheme() {
|
||||
if (settings.THEME.USE_CUSTOM_THEME) {
|
||||
setCurrentTheme();
|
||||
} else {
|
||||
root.style.setProperty('--main-color1', '#6e2c8c');
|
||||
root.style.setProperty('--main-color2', 'white');
|
||||
root.style.setProperty('--main-color3', '#211E1E');
|
||||
root.style.setProperty('--main-color4', '#2f2c34');
|
||||
root.style.setProperty('--top-bar', '#100B12');
|
||||
root.style.setProperty('--mid-section', '#352d3d');
|
||||
root.style.setProperty('--chat-bubble', ' #7A6D7F');
|
||||
root.style.setProperty('--chat-bubble-header', '#141414');
|
||||
root.style.setProperty('--chat-bubble-message', 'white');
|
||||
};
|
||||
if (settings.THEME.USE_CUSTOM_THEME) {
|
||||
setCurrentTheme();
|
||||
} else {
|
||||
root.style.setProperty('--main-color1', '#6e2c8c');
|
||||
root.style.setProperty('--main-color2', 'white');
|
||||
root.style.setProperty('--main-color3', '#211E1E');
|
||||
root.style.setProperty('--main-color4', '#2f2c34');
|
||||
root.style.setProperty('--top-bar', '#100B12');
|
||||
root.style.setProperty('--mid-section', '#352d3d');
|
||||
root.style.setProperty('--chat-bubble', ' #7A6D7F');
|
||||
root.style.setProperty('--chat-bubble-header', '#141414');
|
||||
root.style.setProperty('--chat-bubble-message', 'white');
|
||||
}
|
||||
}
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_1').addEventListener('input', () => {
|
||||
const x = document.getElementById('MAIN_COLOR_1').value;
|
||||
root.style.setProperty('--main-color1-temp', x);
|
||||
console.log(x);
|
||||
const x = document.getElementById('MAIN_COLOR_1').value;
|
||||
root.style.setProperty('--main-color1-temp', x);
|
||||
console.log(x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_1').addEventListener('change', () => {
|
||||
settings.THEME.MAIN_COLOR_1 = document.getElementById('MAIN_COLOR_1').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#MAIN_COLOR_1", settings.THEME.MAIN_COLOR_1, "--main-color1");
|
||||
settings.THEME.MAIN_COLOR_1 = document.getElementById('MAIN_COLOR_1').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#MAIN_COLOR_1', settings.THEME.MAIN_COLOR_1, '--main-color1');
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_2').addEventListener('input', () => {
|
||||
const x = document.getElementById('MAIN_COLOR_2').value;
|
||||
root.style.setProperty('--main-color2-temp', x);
|
||||
const x = document.getElementById('MAIN_COLOR_2').value;
|
||||
root.style.setProperty('--main-color2-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_2').addEventListener('change', () => {
|
||||
settings.THEME.MAIN_COLOR_2 = document.getElementById('MAIN_COLOR_2').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#MAIN_COLOR_2", settings.THEME.MAIN_COLOR_2, "--main-color2");
|
||||
settings.THEME.MAIN_COLOR_2 = document.getElementById('MAIN_COLOR_2').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#MAIN_COLOR_2', settings.THEME.MAIN_COLOR_2, '--main-color2');
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_3').addEventListener('input', () => {
|
||||
const x = document.getElementById('MAIN_COLOR_3').value;
|
||||
root.style.setProperty('--main-color3-temp', x);
|
||||
const x = document.getElementById('MAIN_COLOR_3').value;
|
||||
root.style.setProperty('--main-color3-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_3').addEventListener('change', () => {
|
||||
settings.THEME.MAIN_COLOR_3 = document.getElementById('MAIN_COLOR_3').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#MAIN_COLOR_3", settings.THEME.MAIN_COLOR_3, "--main-color3");
|
||||
settings.THEME.MAIN_COLOR_3 = document.getElementById('MAIN_COLOR_3').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#MAIN_COLOR_3', settings.THEME.MAIN_COLOR_3, '--main-color3');
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_4').addEventListener('input', () => {
|
||||
const x = document.getElementById('MAIN_COLOR_4').value;
|
||||
root.style.setProperty('--main-color4-temp', x);
|
||||
const x = document.getElementById('MAIN_COLOR_4').value;
|
||||
root.style.setProperty('--main-color4-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#MAIN_COLOR_4').addEventListener('change', () => {
|
||||
settings.THEME.MAIN_COLOR_4 = document.getElementById('MAIN_COLOR_4').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#MAIN_COLOR_4", settings.THEME.MAIN_COLOR_4, "--main-color4");
|
||||
settings.THEME.MAIN_COLOR_4 = document.getElementById('MAIN_COLOR_4').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#MAIN_COLOR_4', settings.THEME.MAIN_COLOR_4, '--main-color4');
|
||||
});
|
||||
|
||||
document.body.querySelector('#TOP_BAR').addEventListener('input', () => {
|
||||
const x = document.getElementById('TOP_BAR').value;
|
||||
root.style.setProperty('--top-bar-temp', x);
|
||||
const x = document.getElementById('TOP_BAR').value;
|
||||
root.style.setProperty('--top-bar-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#TOP_BAR').addEventListener('change', () => {
|
||||
settings.THEME.TOP_BAR = document.getElementById('TOP_BAR').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#TOP_BAR", settings.THEME.TOP_BAR, "--top-bar");
|
||||
settings.THEME.TOP_BAR = document.getElementById('TOP_BAR').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#TOP_BAR', settings.THEME.TOP_BAR, '--top-bar');
|
||||
});
|
||||
|
||||
document.body.querySelector('#MID_SECTION').addEventListener('input', () => {
|
||||
const x = document.getElementById('MID_SECTION').value;
|
||||
root.style.setProperty('--mid-section-temp', x);
|
||||
const x = document.getElementById('MID_SECTION').value;
|
||||
root.style.setProperty('--mid-section-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#MID_SECTION').addEventListener('change', () => {
|
||||
settings.THEME.MID_SECTION = document.getElementById('MID_SECTION').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#MID_SECTION", settings.THEME.MID_SECTION, "--mid-section");
|
||||
settings.THEME.MID_SECTION = document.getElementById('MID_SECTION').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#MID_SECTION', settings.THEME.MID_SECTION, '--mid-section');
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_BG').addEventListener('input', () => {
|
||||
const x = document.getElementById('CHAT_BUBBLE_BG').value;
|
||||
root.style.setProperty('--chat-bubble-temp', x);
|
||||
const x = document.getElementById('CHAT_BUBBLE_BG').value;
|
||||
root.style.setProperty('--chat-bubble-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_BG').addEventListener('change', () => {
|
||||
settings.THEME.CHAT_BUBBLE_BG = document.getElementById('CHAT_BUBBLE_BG').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#CHAT_BUBBLE_BG", settings.THEME.CHAT_BUBBLE_BG, "--chat-bubble");
|
||||
settings.THEME.CHAT_BUBBLE_BG = document.getElementById('CHAT_BUBBLE_BG').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#CHAT_BUBBLE_BG', settings.THEME.CHAT_BUBBLE_BG, '--chat-bubble');
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_HEADER').addEventListener('input', () => {
|
||||
const x = document.getElementById('CHAT_BUBBLE_HEADER').value;
|
||||
root.style.setProperty('--chat-bubble-header-temp', x);
|
||||
const x = document.getElementById('CHAT_BUBBLE_HEADER').value;
|
||||
root.style.setProperty('--chat-bubble-header-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_HEADER').addEventListener('change', () => {
|
||||
settings.THEME.CHAT_BUBBLE_HEADER = document.getElementById('CHAT_BUBBLE_HEADER').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#CHAT_BUBBLE_HEADER", settings.THEME.CHAT_BUBBLE_HEADER, "--chat-bubble-header");
|
||||
settings.THEME.CHAT_BUBBLE_HEADER = document.getElementById('CHAT_BUBBLE_HEADER').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#CHAT_BUBBLE_HEADER', settings.THEME.CHAT_BUBBLE_HEADER, '--chat-bubble-header');
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_MESSAGE').addEventListener('input', () => {
|
||||
const x = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
|
||||
root.style.setProperty('--chat-bubble-message-temp', x);
|
||||
const x = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
|
||||
root.style.setProperty('--chat-bubble-message-temp', x);
|
||||
});
|
||||
|
||||
document.body.querySelector('#CHAT_BUBBLE_MESSAGE').addEventListener('change', () => {
|
||||
settings.THEME.CHAT_BUBBLE_MESSAGE = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor("#CHAT_BUBBLE_MESSAGE", settings.THEME.CHAT_BUBBLE_MESSAGE, "--chat-bubble-message");
|
||||
settings.THEME.CHAT_BUBBLE_MESSAGE = document.getElementById('CHAT_BUBBLE_MESSAGE').value;
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
changeColor('#CHAT_BUBBLE_MESSAGE', settings.THEME.CHAT_BUBBLE_MESSAGE, '--chat-bubble-message');
|
||||
});
|
||||
|
||||
module.exports = { setTheme };
|
||||
|
|
|
|||
|
|
@ -54,27 +54,23 @@ function displayTwitchMessage(logoUrl, username, messageObject, fileteredMessage
|
|||
usernameHtml.innerText = username;
|
||||
}
|
||||
|
||||
const postTime = article.querySelector('.post-time');
|
||||
const postTime = document.createElement('span');
|
||||
postTime.classList.add('post-time');
|
||||
|
||||
if (postTime) {
|
||||
postTime.innerText = getPostTime();
|
||||
}
|
||||
|
||||
const msg = article.querySelector('.msg');
|
||||
const msg = article.querySelector('.msg-box');
|
||||
if (msg) {
|
||||
msg.innerHTML = '';
|
||||
|
||||
const messageElement = document.createElement('div');
|
||||
|
||||
messageObject.forEach((entry) => {
|
||||
const messageElement = document.createElement('div');
|
||||
if (entry.text) {
|
||||
messageElement.innerText = entry.text;
|
||||
msg.appendChild(messageElement);
|
||||
msg.innerHTML += entry.text;
|
||||
} else {
|
||||
messageElement.innerHTML = entry.html;
|
||||
msg.appendChild(messageElement);
|
||||
msg.innerHTML += entry.html;
|
||||
}
|
||||
});
|
||||
msg.appendChild(postTime);
|
||||
}
|
||||
|
||||
// Appends the message to the main chat box (shows the message)
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
let SelectedVoice = '';
|
||||
let Encoding = '';
|
||||
let counter = 0;
|
||||
// wrap in promise
|
||||
const speak = (textObject) =>
|
||||
new Promise((resolve) => {
|
||||
// say.setEncoding(Encoding);
|
||||
counter += 1;
|
||||
let savePath = path.join(resourcesPath, './sounds/tts/internal_audio_' + counter + '.mp3');
|
||||
|
||||
say.export(textObject.filtered, SelectedVoice, 1, savePath, (err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
sound.playAudio({ path: savePath, message: textObject });
|
||||
sound.playNotificationSound();
|
||||
}
|
||||
resolve('finished');
|
||||
});
|
||||
});
|
||||
|
||||
// queue system
|
||||
class SayQueue {
|
||||
constructor() {
|
||||
this.messages = [];
|
||||
this.status = 0;
|
||||
}
|
||||
|
||||
async shift() {
|
||||
this.status = 1;
|
||||
while (this.messages.length > 0) {
|
||||
await speak(this.messages.shift(), SelectedVoice, 1);
|
||||
}
|
||||
this.status = 0;
|
||||
}
|
||||
|
||||
add(message, selectedVoice) {
|
||||
this.messages.push(message);
|
||||
SelectedVoice = selectedVoice;
|
||||
if (this.status === 0) {
|
||||
this.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sayQueue = new SayQueue();
|
||||
module.exports = sayQueue;
|
||||
157
src/main.js
|
|
@ -1,6 +1,7 @@
|
|||
const { app, shell, BrowserWindow, ipcMain } = require('electron');
|
||||
const { app, BrowserWindow, ipcMain } = require('electron');
|
||||
const { writeIniFile } = require('write-ini-file');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const ini = require('ini');
|
||||
const fs = require('fs');
|
||||
|
|
@ -13,9 +14,11 @@ let window;
|
|||
|
||||
if (app.isPackaged) {
|
||||
settingsPath = path.join(process.resourcesPath, './settings.ini');
|
||||
pythonPath = path.join(process.resourcesPath, './backend');
|
||||
resourcesPath = process.resourcesPath;
|
||||
} else {
|
||||
settingsPath = path.join(resourcesPath, './config/settings.ini');
|
||||
pythonPath = path.join(resourcesPath, './backend');
|
||||
}
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
|
|
@ -32,11 +35,11 @@ async function createWindow() {
|
|||
}
|
||||
|
||||
window = new BrowserWindow({
|
||||
icon: path.join(__dirname, '/images/icon.png'),
|
||||
width: parseInt(settings.SETTINGS.WIDTH),
|
||||
height: parseInt(settings.SETTINGS.HEIGHT),
|
||||
x: parseInt(settings.SETTINGS.POSITION_X),
|
||||
y: parseInt(settings.SETTINGS.POSITION_Y),
|
||||
icon: path.join(__dirname, '/images/icon-512.png'),
|
||||
width: parseInt(settings.GENERAL.WIDTH),
|
||||
height: parseInt(settings.GENERAL.HEIGHT),
|
||||
x: parseInt(settings.GENERAL.POSITION_X),
|
||||
y: parseInt(settings.GENERAL.POSITION_Y),
|
||||
frame: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
|
|
@ -44,7 +47,6 @@ async function createWindow() {
|
|||
enableRemoteModule: true,
|
||||
},
|
||||
});
|
||||
window.loadURL('https://github.com');
|
||||
|
||||
window.loadFile(path.join(__dirname, 'index.html'));
|
||||
|
||||
|
|
@ -56,10 +58,10 @@ async function createWindow() {
|
|||
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8')); // load newest settings in case anything changed after starting the program
|
||||
const bounds = window.getBounds();
|
||||
|
||||
settings.SETTINGS.WIDTH = bounds.width;
|
||||
settings.SETTINGS.HEIGHT = bounds.height;
|
||||
settings.SETTINGS.POSITION_X = bounds.x;
|
||||
settings.SETTINGS.POSITION_Y = bounds.y;
|
||||
settings.GENERAL.WIDTH = bounds.width;
|
||||
settings.GENERAL.HEIGHT = bounds.height;
|
||||
settings.GENERAL.POSITION_X = bounds.x;
|
||||
settings.GENERAL.POSITION_Y = bounds.y;
|
||||
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
});
|
||||
|
|
@ -76,13 +78,15 @@ app.on('window-all-closed', (event) => {
|
|||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
window.webContents.send('quit-event');
|
||||
});
|
||||
|
||||
ipcMain.on('resize-window', (event, width, height) => {
|
||||
const browserWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
browserWindow.setSize(width, height);
|
||||
|
|
@ -114,99 +118,12 @@ ipcMain.on('restart', (event) => {
|
|||
});
|
||||
|
||||
ipcMain.on('environment', (event) => {
|
||||
event.returnValue = { resourcesPath: resourcesPath, settingsPath: settingsPath, settings: settings, isPackaged: app.isPackaged };
|
||||
});
|
||||
|
||||
let twitchAuthentication = () =>
|
||||
new Promise((resolve) => {
|
||||
const http = require('http');
|
||||
const redirectUri = 'http://localhost:1989/auth';
|
||||
const scopes = ['chat:edit', 'chat:read'];
|
||||
|
||||
const express = require('express');
|
||||
let tempAuthServer = express();
|
||||
const port = 1989;
|
||||
|
||||
const { parse: parseQueryString } = require('querystring');
|
||||
|
||||
tempAuthServer.use(function (req, res, next) {
|
||||
if (req.url !== '/auth') {
|
||||
let token = parseQueryString(req.query.auth);
|
||||
settings.TWITCH.OAUTH_TOKEN = token['#access_token'];
|
||||
fs.writeFileSync(settingsPath, ini.stringify(settings));
|
||||
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
resolve('finished');
|
||||
stopServer();
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
function stopServer() {
|
||||
tempAuthServer.close();
|
||||
}
|
||||
|
||||
const htmlString = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Authentication</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Authentication successful! You can close this window now.</h1>
|
||||
<form name="auth" "action="auth" method="get" >
|
||||
<input type="text" id="auth" name="auth"/>
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
function onSubmitComplete() {
|
||||
close();
|
||||
}
|
||||
document.querySelector("#auth").value = document.location.hash;
|
||||
document.auth.submit();
|
||||
setTimeout(onSubmitComplete, 500);
|
||||
</script>
|
||||
`;
|
||||
|
||||
tempAuthServer.get('/auth', (req, res) => {
|
||||
res.send(htmlString);
|
||||
});
|
||||
|
||||
tempAuthServer.post('/auth', (req, res) => {
|
||||
res.render('authentication', { name: req.body.name });
|
||||
});
|
||||
|
||||
const server = http.createServer(tempAuthServer);
|
||||
|
||||
server.listen(port, () => {
|
||||
const authURL = `https://id.twitch.tv/oauth2/authorize?client_id=${settings.TWITCH.CLIENT_ID}&redirect_uri=${encodeURIComponent(
|
||||
redirectUri,
|
||||
)}&response_type=token&scope=${scopes.join(' ')}`;
|
||||
shell.openExternal(authURL);
|
||||
});
|
||||
|
||||
function stopServer() {
|
||||
server.close(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('twitch', async (event) => {
|
||||
await twitchAuthentication();
|
||||
event.returnValue = settings.TWITCH.OAUTH_TOKEN;
|
||||
});
|
||||
|
||||
ipcMain.on('vtuber', async (event) => {
|
||||
shell.openExternal(`http://localhost:${settings.SERVER.PORT}/vtuber/`);
|
||||
});
|
||||
|
||||
ipcMain.on('chatBubble', async (event) => {
|
||||
shell.openExternal(`http://localhost:${settings.SERVER.PORT}/chat/`);
|
||||
event.returnValue = { resourcesPath, pythonPath, settingsPath, settings, isPackaged: app.isPackaged };
|
||||
});
|
||||
|
||||
async function createIniFile() {
|
||||
await writeIniFile(settingsPath, {
|
||||
SETTINGS: {
|
||||
GENERAL: {
|
||||
VOICE_ENABLED: true,
|
||||
NOTIFICATION_ENABLED: true,
|
||||
POSITION_X: 0,
|
||||
|
|
@ -214,18 +131,26 @@ async function createIniFile() {
|
|||
WIDTH: 1024,
|
||||
HEIGHT: 768,
|
||||
LANGUAGE: 'EN',
|
||||
PORT: 9000,
|
||||
VIEWERS_PANEL: false,
|
||||
LOCATION: pythonPath,
|
||||
},
|
||||
LANGUAGE: {
|
||||
USE_DETECTION: false,
|
||||
},
|
||||
TTS: {
|
||||
USE_TTS: true,
|
||||
PRIMARY_TTS_VOICE: 0,
|
||||
PRIMARY_TTS_NAME: '',
|
||||
PRIMARY_VOICE: '',
|
||||
PRIMARY_TTS_LANGUAGE: 'EN',
|
||||
PRIMARY_TTS_LANGUAGE_INDEX: 0,
|
||||
SECONDARY_TTS_VOICE: 0,
|
||||
SECONDARY_TTS_NAME: '',
|
||||
SECONDARY_VOICE: '',
|
||||
SECONDARY_TTS_LANGUAGE: 'EN',
|
||||
SECONDARY_TTS_LANGUAGE_INDEX: 0,
|
||||
TTS_VOLUME: 50,
|
||||
},
|
||||
STT: {
|
||||
USE_STT: false,
|
||||
MICROPHONE_ID: 'default',
|
||||
SELECTED_MICROPHONE: 'default',
|
||||
MICROPHONE: 0,
|
||||
LANGUAGE: 'vosk-model-small-es-0.42',
|
||||
},
|
||||
AUDIO: {
|
||||
USE_NOTIFICATION_SOUNDS: true,
|
||||
|
|
@ -234,6 +159,7 @@ async function createIniFile() {
|
|||
NOTIFICATION_VOLUME: 50,
|
||||
SELECTED_TTS_AUDIO_DEVICE: 0,
|
||||
TTS_AUDIO_DEVICE: 'default',
|
||||
TTS_VOLUME: 50,
|
||||
},
|
||||
THEME: {
|
||||
USE_CUSTOM_THEME: false,
|
||||
|
|
@ -254,20 +180,25 @@ async function createIniFile() {
|
|||
OAUTH_TOKEN: '',
|
||||
CLIENT_ID: '2t206sj7rvtr1rutob3p627d13jch9',
|
||||
},
|
||||
SERVER: {
|
||||
USE_SERVER: false,
|
||||
PORT: '9000',
|
||||
MODULES: {
|
||||
USE_MODULES: false,
|
||||
USE_VTUBER: false,
|
||||
USE_CHATBUBBLE: false,
|
||||
},
|
||||
AMAZON: {
|
||||
USE_TWITCH: false,
|
||||
USE_AMAZON: false,
|
||||
ACCESS_KEY: '',
|
||||
ACCESS_SECRET: '',
|
||||
PRIMARY_VOICE: '',
|
||||
SECONDARY_VOICE: '',
|
||||
CHARACTERS_USED: 0,
|
||||
},
|
||||
GOOGLE: {
|
||||
USE_GOOGLE: false,
|
||||
API_KEY: '',
|
||||
PRIMARY_VOICE: '',
|
||||
SECONDARY_VOICE: '',
|
||||
CHARACTERS_USED: 0,
|
||||
},
|
||||
}).then(() => {
|
||||
settings = ini.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
<script
|
||||
src="https://cdn.socket.io/4.6.0/socket.io.min.js"
|
||||
integrity="sha384-c79GN5VsunZvi+Q/WObgk2in0CbZsHnjEqvFxC5DxHn9lTfNce2WW6h2pH6u/kF+"
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<link rel="stylesheet" href="./fonts/FRAMCDN/font.css"/>
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<link rel="stylesheet" href="./fonts/FRAMCDN/font.css" />
|
||||
<link href="main.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -18,5 +18,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<script src="main.js"></script>
|
||||
<video id="camera" autoplay></video>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,173 +1,171 @@
|
|||
body {
|
||||
background-color: transparent;
|
||||
font-family: 'FRAMDCN';
|
||||
background-color: transparent;
|
||||
font-family: 'FRAMDCN';
|
||||
}
|
||||
|
||||
:root {
|
||||
--variable: 2s;
|
||||
--buttonBackground: #bf2c2c;
|
||||
--variable: 2s;
|
||||
--buttonBackground: #bf2c2c;
|
||||
}
|
||||
|
||||
.thomas {
|
||||
position: relative;
|
||||
float: center;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
float: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
.speechbubble {
|
||||
display: block;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
display: block;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.fade-outx {
|
||||
animation: fade-outx var(--variable) linear;
|
||||
animation: fade-outx var(--variable) linear;
|
||||
}
|
||||
|
||||
@keyframes fade-outx {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-outxx {
|
||||
animation: fade-outxx var(--variable) linear;
|
||||
animation: fade-outxx var(--variable) linear;
|
||||
}
|
||||
|
||||
@keyframes fade-outxx {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bounce-in {
|
||||
animation: bounce-in 1s ease;
|
||||
animation: bounce-in 1s ease;
|
||||
}
|
||||
|
||||
@keyframes bounce-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(.3);
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(.9);
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.bounce-inx {
|
||||
animation: bounce-inx 1s ease;
|
||||
animation: bounce-inx 1s ease;
|
||||
}
|
||||
|
||||
@keyframes bounce-inx {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.msg-container {
|
||||
margin-bottom: 10px;
|
||||
align-self: center;
|
||||
position: static;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.message-window {
|
||||
height: calc(100% - 50px);
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
background: transparent;
|
||||
height: calc(100% - 50px);
|
||||
overflow: hidden;
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.message-window::before {
|
||||
content: '';
|
||||
flex: 1 0 0px;
|
||||
content: '';
|
||||
flex: 1 0 0px;
|
||||
}
|
||||
|
||||
.OptionPanel {
|
||||
/* visibility: hidden; */
|
||||
flex: 3;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 25px);
|
||||
background: transparent;
|
||||
flex: 3;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 25px);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.OptionPanel.show {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.message {
|
||||
text-align: left;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
min-width: 125px;
|
||||
hyphens: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
float: right;
|
||||
overflow-wrap: break-word;
|
||||
text-align: left;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
min-width: 125px;
|
||||
hyphens: auto;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
float: right;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
.message {
|
||||
position: relative;
|
||||
border: 2px solid #ff80e1;
|
||||
box-shadow: 0 2px 10px rgba(255, 128, 225, 0.5);
|
||||
background: linear-gradient(45deg, rgb(15, 12, 41,0.7), rgb(48, 43, 99,0.7));
|
||||
/* background: linear-gradient(45deg, rgba(72, 0, 154, 0.7), rgba(138, 43, 226, 0.7)); */
|
||||
position: relative;
|
||||
border: 2px solid #ff80e1;
|
||||
box-shadow: 0 2px 10px rgba(255, 128, 225, 0.5);
|
||||
background: linear-gradient(45deg, rgb(15, 12, 41, 0.7), rgb(48, 43, 99, 0.7));
|
||||
/* background: linear-gradient(45deg, rgba(72, 0, 154, 0.7), rgba(138, 43, 226, 0.7)); */
|
||||
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 20px;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.message::after {
|
||||
|
||||
}
|
||||
|
||||
.arrow{
|
||||
content: "";
|
||||
border: 2px solid #ff80e1;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
transform: translateX(-50%) rotate(180deg);
|
||||
border-width: 10px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgb(255, 128, 225,0.7) transparent;
|
||||
color: #ff80e1
|
||||
.arrow {
|
||||
content: '';
|
||||
border: 2px solid #ff80e1;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
transform: translateX(-50%) rotate(180deg);
|
||||
border-width: 10px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent rgb(255, 128, 225, 0.7) transparent;
|
||||
color: #ff80e1;
|
||||
}
|
||||
|
||||
.sender{
|
||||
color: #ff80e1;
|
||||
font-size: 14pt;
|
||||
}
|
||||
.sender {
|
||||
color: #ff80e1;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Connect to the Socket.IO server
|
||||
const socket = io();
|
||||
|
||||
|
|
@ -34,11 +33,11 @@ let fullMessageLength = 0;
|
|||
|
||||
function getFullMessageLength(text) {
|
||||
let fullMessageLength = 0;
|
||||
text.forEach(element => {
|
||||
text.forEach((element) => {
|
||||
if (element.text) {
|
||||
fullMessageLength += element.text.length;
|
||||
}
|
||||
element.html
|
||||
element.html;
|
||||
fullMessageLength += 1;
|
||||
});
|
||||
|
||||
|
|
@ -53,7 +52,6 @@ function streamText() {
|
|||
// setTimeout(streamText, 50);
|
||||
// }
|
||||
if (currentIndex < messageStream.length) {
|
||||
|
||||
textStreamContainer.innerHTML += messageStream.charAt(currentIndex);
|
||||
currentIndex++;
|
||||
setTimeout(streamText, 50);
|
||||
|
|
@ -88,7 +86,7 @@ function displayTwitchMessage(logoUrl, username, messageObject) {
|
|||
article.innerHTML = placeMessage;
|
||||
const msg = article.querySelector('.message');
|
||||
|
||||
msg.innerHTML = `<div class="sender">${username}</div>`//\n${message}`;
|
||||
msg.innerHTML = `<div class="sender">${username}</div>`; //\n${message}`;
|
||||
|
||||
msg.style.fontSize = '12pt';
|
||||
|
||||
|
|
|
|||