diff --git a/forge.config.js b/forge.config.js index e2850c8..747e0c1 100644 --- a/forge.config.js +++ b/forge.config.js @@ -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', './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: [ diff --git a/languages.txt b/languages.txt new file mode 100644 index 0000000..89df109 --- /dev/null +++ b/languages.txt @@ -0,0 +1 @@ +{'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è': {'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å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'}} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 88b67d4..7095942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "loquendo-bot", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "loquendo-bot", - "version": "2.1.0", + "version": "2.2.0", "license": "ISC", "dependencies": { "axios": "^1.4.0", @@ -14,6 +14,7 @@ "express": "^4.18.2", "franc": "^6.1.0", "i18next-electron-language-detector": "^0.0.10", + "iconv-lite": "^0.6.3", "ini": "^2.0.0", "kill-port": "^2.0.1", "langdetect": "^0.2.1", @@ -26,6 +27,7 @@ "sound-play": "^1.1.0", "tmi.js": "^1.8.5", "url": "^0.11.1", + "voice-recognition": "^1.0.6", "winston": "^3.10.0", "write-ini-file": "^4.0.1" }, @@ -1180,6 +1182,14 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1228,6 +1238,17 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2513,18 +2534,6 @@ "iconv-lite": "^0.6.2" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -2899,6 +2908,11 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -3681,11 +3695,11 @@ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -4633,6 +4647,17 @@ "ms": "^2.1.1" } }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -5356,6 +5381,17 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rcedit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-3.0.2.tgz", @@ -6709,6 +6745,14 @@ "extsprintf": "^1.2.0" } }, + "node_modules/voice-recognition": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/voice-recognition/-/voice-recognition-1.0.6.tgz", + "integrity": "sha512-y0DcHDoWx2Kw21WsshjL8WDT6qbAPmqIW9a0bnwQwJ0xJ6/RqIUqCdjNEcoXbC4tUnf05S1h/gWT76Zb2toCNw==", + "dependencies": { + "bindings": "^1.5.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index b377841..042c640 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "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\"" + "lint": "echo \"No linting configured\"", + "backend": "pyinstaller --noconsole --onefile --collect-all vosk --distpath ./backend ./src/backend/loquendoBot_backend.py" }, "keywords": [], "author": { @@ -22,6 +23,7 @@ "express": "^4.18.2", "franc": "^6.1.0", "i18next-electron-language-detector": "^0.0.10", + "iconv-lite": "^0.6.3", "ini": "^2.0.0", "kill-port": "^2.0.1", "langdetect": "^0.2.1", @@ -34,6 +36,7 @@ "sound-play": "^1.1.0", "tmi.js": "^1.8.5", "url": "^0.11.1", + "voice-recognition": "^1.0.6", "winston": "^3.10.0", "write-ini-file": "^4.0.1" }, diff --git a/src/config/languages.txt b/src/config/languages.txt deleted file mode 100644 index 73adeae..0000000 --- a/src/config/languages.txt +++ /dev/null @@ -1,3 +0,0 @@ -EN -ES -NL \ No newline at end of file diff --git a/src/css/chat.css b/src/css/chat.css index b41a751..d01cd88 100644 --- a/src/css/chat.css +++ b/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,"); 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); -} \ No newline at end of file +} diff --git a/src/css/home.css b/src/css/home.css index cf04350..b612164 100644 --- a/src/css/home.css +++ b/src/css/home.css @@ -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); -} \ No newline at end of file +} diff --git a/src/css/menu.css b/src/css/menu.css index 0a5b65d..efdfed2 100644 --- a/src/css/menu.css +++ b/src/css/menu.css @@ -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%; -} \ No newline at end of file +} diff --git a/src/css/sliders.css b/src/css/sliders.css index f9eea45..ce80bf7 100644 --- a/src/css/sliders.css +++ b/src/css/sliders.css @@ -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; } diff --git a/src/css/tabs.css b/src/css/tabs.css index 47cef1b..e634850 100644 --- a/src/css/tabs.css +++ b/src/css/tabs.css @@ -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; } diff --git a/src/css/tts-menu.css b/src/css/tts-menu.css index a44cfe8..fd36954 100644 --- a/src/css/tts-menu.css +++ b/src/css/tts-menu.css @@ -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%); } diff --git a/src/images/amazon.png b/src/images/amazon.png new file mode 100644 index 0000000..460762c Binary files /dev/null and b/src/images/amazon.png differ diff --git a/src/images/amazon.svg b/src/images/amazon.svg deleted file mode 100644 index 4f35504..0000000 --- a/src/images/amazon.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Amazon-color - Created with Sketch. - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/images/google.png b/src/images/google.png new file mode 100644 index 0000000..6e4f2c2 Binary files /dev/null and b/src/images/google.png differ diff --git a/src/images/google.svg b/src/images/google.svg deleted file mode 100644 index 6678410..0000000 --- a/src/images/google.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Google-color - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/images/icon-256.png b/src/images/icon-256.png new file mode 100644 index 0000000..87b48b5 Binary files /dev/null and b/src/images/icon-256.png differ diff --git a/src/images/icon-512.png b/src/images/icon-512.png new file mode 100644 index 0000000..8066012 Binary files /dev/null and b/src/images/icon-512.png differ diff --git a/src/images/icon.ico b/src/images/icon.ico index 7303011..84862b2 100644 Binary files a/src/images/icon.ico and b/src/images/icon.ico differ diff --git a/src/images/icon.png b/src/images/icon.png deleted file mode 100644 index c145646..0000000 Binary files a/src/images/icon.png and /dev/null differ diff --git a/src/images/note.svg b/src/images/note.svg deleted file mode 100644 index 8f060d3..0000000 --- a/src/images/note.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/images/server.png b/src/images/server.png new file mode 100644 index 0000000..c4fa629 Binary files /dev/null and b/src/images/server.png differ diff --git a/src/images/server.svg b/src/images/server.svg deleted file mode 100644 index a2db046..0000000 --- a/src/images/server.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/images/settings.png b/src/images/settings.png new file mode 100644 index 0000000..174670a Binary files /dev/null and b/src/images/settings.png differ diff --git a/src/images/sound.png b/src/images/sound.png new file mode 100644 index 0000000..62f4fe5 Binary files /dev/null and b/src/images/sound.png differ diff --git a/src/images/stt.png b/src/images/stt.png new file mode 100644 index 0000000..4edace5 Binary files /dev/null and b/src/images/stt.png differ diff --git a/src/images/stt.svg b/src/images/stt.svg deleted file mode 100644 index 2b35389..0000000 --- a/src/images/stt.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - -Icon_24px_SpeechtoText_Color - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/images/theme.png b/src/images/theme.png new file mode 100644 index 0000000..e4f4f40 Binary files /dev/null and b/src/images/theme.png differ diff --git a/src/images/translate.png b/src/images/translate.png new file mode 100644 index 0000000..e9a1f2d Binary files /dev/null and b/src/images/translate.png differ diff --git a/src/images/tts.png b/src/images/tts.png new file mode 100644 index 0000000..7512b4f Binary files /dev/null and b/src/images/tts.png differ diff --git a/src/images/tts.svg b/src/images/tts.svg deleted file mode 100644 index 6d20ed6..0000000 --- a/src/images/tts.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - -Icon_24px_TexttoSpeech_Color - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/images/twitch.png b/src/images/twitch.png new file mode 100644 index 0000000..0b6cb45 Binary files /dev/null and b/src/images/twitch.png differ diff --git a/src/images/twitch.svg b/src/images/twitch.svg deleted file mode 100644 index c6b96f9..0000000 --- a/src/images/twitch.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/index.html b/src/index.html index 55a4d06..64b444b 100644 --- a/src/index.html +++ b/src/index.html @@ -29,36 +29,33 @@ +
-
- 🇬🇧 - EN -
-
🇳🇱 NL
-
🇪🇞 ES
-
🇬🇧 EN
-
+
+ +  +
+
+ +
-
+
-
+
-
+
-
- -
@@ -68,17 +65,26 @@
  • -
  • - +
  • + +
  • +
  • + +
  • +
  • +
  • -
  • +
  • -
  • - +
  • + +
  • +
  • +
  • @@ -120,39 +126,34 @@
    -
    - -
    -
    -
    - -  -
    Enable TTS
    - - +
    + +  +
    General settings
    -
    -
    Primary TTS Voice
    - - +
    +
    Port
    + +
    -
    -
    Secondary TTS Voice
    - - +
    +
    Default TTS Service
    +
    -
    +
    +
    2nd TTS Service
    + +
    +
    +
    TTS Output Device
    + +
    +
    TTS Volume
    @@ -162,25 +163,116 @@
    +
    + +
    + +  + + +
    Enable STT
    +
    +
    +
    STT Input Device
    + +
    +
    +
    Voice Language
    + +
    +
    + +
    + + + + +
    Enable Language detection
    +
    +
    +
    Language detection service
    + +
    +
    +
    Translate incoming chat messages to
    + + + +
    +
    +
    Default TTS language
    + +
    +
    +
    2nd TTS language
    + +
    +
    + +
    + +  + + +
    Enable internal TTS
    +
    -
    Test TTS
    - -
    - +
    Default Internal Voice
    + +
    +
    +
    Test default Internal Voice
    + +
    +
    -
    TTS Output Device
    - +
    2nd Internal Voice
    + +
    +
    +
    Test 2nd Internal Voice
    + +
    + + +
    -  -
    Enable notification sounds
    +  +
    Enable notification sounds
    Notification Volume
    @@ -194,7 +286,7 @@
    Notification Sound
    - +
    +
    -  - -
    Enable Twitch
    +  +
    Enable Twitch
    Channel Name
    @@ -224,9 +316,15 @@
    Oauth Token
    - +
    +
    - -
    Enable Server
    - - + + + +
    Enable Modules
    -
    -
    Port
    - - -
    Use Vtuber
    @@ -283,19 +373,20 @@ >
    +
    - -
    Enable Amazon TTS
    + +
    Enable Amazon services
    Access Key
    +
    +
    Characters used
    + + +
    +
    +
    Default Amazon Voice
    + +
    +
    +
    Test Default Amazon Voice
    + +
    + + +
    +
    +
    +
    2nd Amazon Voice
    + +
    +
    +
    Test 2nd Amazon Voice
    + +
    + + +
    +
    +
    - -
    Enable Google TTS
    + +
    Enable Google services
    API Key
    +
    +
    Characters used
    + + +
    +
    +
    Default Google Voice
    + +
    +
    +
    Test default Google Voice
    + +
    + + +
    +
    +
    +
    2nd Google Voice
    + +
    +
    +
    Test 2nd Google Voice
    + +
    + + +
    +
    -
    -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    Enable Custom Theme
    - - -
    -
    - -
    Main Color 1
    -
    -
    - -
    Main Color 2
    -
    -
    - -
    Main Color 3
    -
    -
    - -
    Main Color 4
    -
    -
    - -
    Top Bar
    -
    -
    - -
    Mid Section
    -
    -
    - -
    Chat Bubble Background
    -
    -
    - -
    Chat Bubble Header
    -
    -
    - -
    Chat Bubble Message
    -
    -
    -
    -
    @@ -439,6 +535,71 @@
    + +
    +
    +
    + + +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + +
    Enable Custom Theme
    +
    +
    + +
    Main Color 1
    +
    +
    + +
    Main Color 2
    +
    +
    + +
    Main Color 3
    +
    +
    + +
    Main Color 4
    +
    +
    + +
    Top Bar
    +
    +
    + +
    Mid Section
    +
    +
    + +
    Chat Bubble Background
    +
    +
    + +
    Chat Bubble Header
    +
    +
    + +
    Chat Bubble Message
    +
    +
    +
    +
    + +
    @@ -446,8 +607,8 @@
    @@ -472,7 +633,7 @@
    -
    +
    diff --git a/src/js/amazon.js b/src/js/amazon.js index 4af3e21..fd2de7a 100644 --- a/src/js/amazon.js +++ b/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; diff --git a/src/js/auth.js b/src/js/auth.js index d80933c..7aed6e0 100644 --- a/src/js/auth.js +++ b/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 = ` -// -// -// -// Authentication -// -// -//

    Authentication successful! You can close this window now.

    -//
    -// -// -//
    -// -// -// `; + 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 = ` + + + + Authentication + + +

    Authentication successful! You can close this window now.

    +
    + +
    + + + + `; -// 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"); -}); \ No newline at end of file + 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 }; diff --git a/src/js/backend.js b/src/js/backend.js new file mode 100644 index 0000000..2bac31c --- /dev/null +++ b/src/js/backend.js @@ -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 getSTT() { + 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']); + // python = spawn(path.join(pythonPath, './loquendoBot_backend.exe'), [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(); + getSTT(); + }); + } catch (error) { + console.error('Error during backend initialization:', error); + } + // setTimeout(getSTT, 3000); +} + +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 }; diff --git a/src/js/chat.js b/src/js/chat.js index 5925b9a..f6f1eff 100644 --- a/src/js/chat.js +++ b/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,51 @@ 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')) { + // panel.classList.remove('show'); + } else { + panel.classList.add('show'); + } + }, + { + capture: true, + }, + ); }; displayPanel('.OptionPanel', '#Configuration', '#btnConfiguration'); @@ -118,27 +101,34 @@ displayPanel('.OptionPanel', '#Logs', '#btnLogs'); displayPanel('.OptionPanel', '#BrowsersourceChat', '#btnBrowsersourceChat'); displayPanel('.OptionPanel', '#BrowsersourceVtuber', '#btnBrowsersourceVtuber'); displayPanel('.OptionPanel', '#Chat', '#btnChat'); +displayPanel('.OptionPanel', '#ThemeCreator', '#btnThemeCreator'); +displayPanel('.OptionPanel', '#ChatCreator', '#btnChatCreator'); +// displayPanel('.OptionPanel', '#Info', '#btnInfo'); // #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')) { + // panel.classList.remove('item-active'); + } else { + panel.classList.add('item-active'); + } + }, + { + capture: true, + }, + ); }; displayPanelX('.item', '#btnChat', '#btnChat'); @@ -146,20 +136,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); -}); diff --git a/src/js/google.js b/src/js/google.js index e69de29..9edfa68 100644 --- a/src/js/google.js +++ b/src/js/google.js @@ -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(); +} diff --git a/src/js/languages.js b/src/js/languages.js new file mode 100644 index 0000000..3f7e0fc --- /dev/null +++ b/src/js/languages.js @@ -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ᅵ': { 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ᅵ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 }; diff --git a/src/js/mediaDevices.js b/src/js/mediaDevices.js new file mode 100644 index 0000000..e62a71f --- /dev/null +++ b/src/js/mediaDevices.js @@ -0,0 +1,38 @@ +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) => { + microphones.forEach((mic, i) => { + 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; + } + }); + }) + .catch((error) => { + console.error('Error retrieving microphones:', error); + }); diff --git a/src/js/messageTemplates.js b/src/js/messageTemplates.js index 888c39b..d699652 100644 --- a/src/js/messageTemplates.js +++ b/src/js/messageTemplates.js @@ -1,22 +1,11 @@ const twitchTemplate = ` -
    -
    - - -
    -
    -
    -
    - - - - -
    -

    -
    -
    -
    -
    +
    + + +
    + +
    +
    `.trim(); const userTemplate = ` @@ -31,7 +20,6 @@ const userTemplate = ` You -

    diff --git a/src/js/renderer.js b/src/js/renderer.js index 2944c5d..cce849c 100644 --- a/src/js/renderer.js +++ b/src/js/renderer.js @@ -2,7 +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 { ipcRenderer, shell } = require('electron'); // necessary electron libraries to send data to the app const say = require('say'); const request = require('request'); const langdetect = require('langdetect'); @@ -15,11 +15,13 @@ const GoogleTTS = require('node-google-tts-api'); const tts = new GoogleTTS(); const { Socket } = require('socket.io-client'); +// const { languages } = require('./languages'); 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 +29,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 +36,40 @@ 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(); @@ -105,6 +104,24 @@ fs.readdir(notificationSounds, (err, files) => { notificationSound.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND; }); +// Check for installed stt models +fs.readdir(sttModels, (err, files) => { + files.forEach((file, i) => { + // Create a new option element. + const option = document.createElement('option'); + + // Set the options value and text. + option.value = i; + option.innerHTML = file; + + // Add the option to the sound selector. + sttModel.appendChild(option); + }); + + // set the saved notification sound + sttModel.selectedIndex = settings.AUDIO.NOTIFICATION_SOUND; +}); + async function getAudioDevices() { if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) { // logger.info('enumerateDevices() not supported.'); @@ -129,122 +146,38 @@ 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'); + for (const language in languageObject.languages) { + if (languageObject.languages.hasOwnProperty(language)) { + const iso639 = languageObject.languages[language]['ISO-639']; + // console.log(`${language}: ${iso639}`); - option.value = language; - option.innerHTML = languagesObject[language]; - - languageSelect.appendChild(option); - }); + 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 +203,16 @@ 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); + // console.log(style.getPropertyValue('width')); + // console.log(style.getPropertyValue('width') + style.getPropertyValue('width') + 10); + + const msg = article.querySelector('.msg-box'); + // msg.width = `${getComputedStyle(usernameHtml).width + getComputedStyle(usernameHtml).width / 10}px`; + const messages = Array.from(document.body.querySelectorAll('.msg-container')); const lastMessage = messages[messages.length - 1]; lastMessage.scrollIntoView({ behavior: 'smooth' }); @@ -279,8 +222,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 +254,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; -// }); diff --git a/src/js/server.js b/src/js/server.js index ac6a2e8..8d9aa47 100644 --- a/src/js/server.js +++ b/src/js/server.js @@ -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,35 @@ 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) { + // internalTTS.getInstalledVoices(); + } }); // Handle socket connections @@ -78,4 +80,4 @@ io.on('connection', (socket) => { socket.on('disconnect', () => {}); }); -module.exports = { startVtuber, startChatBubble }; +module.exports = { startVtuberModule, startChatBubbleModule }; diff --git a/src/js/settings.js b/src/js/settings.js index 5adc41e..3c005f6 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -1,23 +1,103 @@ +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.selectedIndex; + settings.STT.MICROPHONE_ID = select.options[select.selectedIndex].text; + fs.writeFileSync(settingsPath, ini.stringify(settings)); + createNotification('Saved microphone!', 'success'); + startVoiceRecognition(); +}); + +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 +137,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 +166,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 +242,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 +368,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 +427,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 +518,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 +538,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 +547,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 +556,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, }; diff --git a/src/js/sound.js b/src/js/sound.js index e4421b1..c627d27 100644 --- a/src/js/sound.js +++ b/src/js/sound.js @@ -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,56 @@ 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 }; diff --git a/src/js/theme.js b/src/js/theme.js index 68e8519..d324b87 100644 --- a/src/js/theme.js +++ b/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 }; diff --git a/src/js/twitch.js b/src/js/twitch.js index 09bfe5a..e9da6ab 100644 --- a/src/js/twitch.js +++ b/src/js/twitch.js @@ -1,5 +1,6 @@ const tmi = require('tmi.js'); const axios = require('axios'); +const { post } = require('request'); let client; @@ -54,27 +55,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) diff --git a/src/js/voiceQueue.js b/src/js/voiceQueue.js index cee80bd..a767447 100644 --- a/src/js/voiceQueue.js +++ b/src/js/voiceQueue.js @@ -1,3 +1,5 @@ +var iconv = require('iconv-lite'); + let SelectedVoice = ''; let Encoding = ''; let counter = 0; @@ -8,7 +10,7 @@ const speak = (textObject) => counter += 1; let savePath = path.join(resourcesPath, './sounds/tts/internal_audio_' + counter + '.mp3'); - say.export(textObject.filtered, SelectedVoice, 1, savePath, (err) => { + say.export(iconv.encode(textObject.filtered, 'ascii'), SelectedVoice, 1, savePath, (err) => { if (err) { console.error(err); } else { diff --git a/src/main.js b/src/main.js index ab964c5..f2d3320 100644 --- a/src/main.js +++ b/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 = ` - - - - Authentication - - -

    Authentication successful! You can close this window now.

    -
    - - -
    - - - - `; - - 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,25 @@ 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: 5, }, AUDIO: { USE_NOTIFICATION_SOUNDS: true, @@ -234,6 +158,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 +179,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')); diff --git a/src/modules/chat/index.html b/src/modules/chat/index.html index e6fc879..c961cd4 100644 --- a/src/modules/chat/index.html +++ b/src/modules/chat/index.html @@ -5,9 +5,9 @@ - + crossorigin="anonymous" + > + @@ -18,5 +18,6 @@ + diff --git a/src/modules/chat/main.css b/src/modules/chat/main.css index 6fcb85f..bea4e5b 100644 --- a/src/modules/chat/main.css +++ b/src/modules/chat/main.css @@ -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; -} \ No newline at end of file +.sender { + color: #ff80e1; + font-size: 14pt; +} diff --git a/src/modules/chat/main.js b/src/modules/chat/main.js index 6da62ef..d4e92b9 100644 --- a/src/modules/chat/main.js +++ b/src/modules/chat/main.js @@ -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 = `
    ${username}
    `//\n${message}`; + msg.innerHTML = `
    ${username}
    `; //\n${message}`; msg.style.fontSize = '12pt'; diff --git a/src/sounds/notifications/electric.mp3 b/src/sounds/notifications/electric.mp3 deleted file mode 100644 index f9bf4f8..0000000 Binary files a/src/sounds/notifications/electric.mp3 and /dev/null differ diff --git a/src/sounds/notifications/morse.mp3 b/src/sounds/notifications/morse.mp3 deleted file mode 100644 index 04f4797..0000000 Binary files a/src/sounds/notifications/morse.mp3 and /dev/null differ diff --git a/src/sounds/notifications/musicbox.mp3 b/src/sounds/notifications/musicbox.mp3 deleted file mode 100644 index c58e664..0000000 Binary files a/src/sounds/notifications/musicbox.mp3 and /dev/null differ diff --git a/src/sounds/tts/coin.mp3 b/src/sounds/tts/coin.mp3 deleted file mode 100644 index 806ee8b..0000000 Binary files a/src/sounds/tts/coin.mp3 and /dev/null differ diff --git a/src/sounds/tts/internal_audio_24.mp3 b/src/sounds/tts/internal_audio_24.mp3 deleted file mode 100644 index 3995741..0000000 Binary files a/src/sounds/tts/internal_audio_24.mp3 and /dev/null differ diff --git a/src/sounds/tts/internal_audio_25.mp3 b/src/sounds/tts/internal_audio_25.mp3 deleted file mode 100644 index 3995741..0000000 Binary files a/src/sounds/tts/internal_audio_25.mp3 and /dev/null differ diff --git a/src/sounds/tts/internal_audio_26.mp3 b/src/sounds/tts/internal_audio_26.mp3 deleted file mode 100644 index 937b401..0000000 Binary files a/src/sounds/tts/internal_audio_26.mp3 and /dev/null differ diff --git a/src/sounds/tts/internal_audio_27.mp3 b/src/sounds/tts/internal_audio_27.mp3 deleted file mode 100644 index f76fc65..0000000 Binary files a/src/sounds/tts/internal_audio_27.mp3 and /dev/null differ