From 6ba332b323936881affe33bee4510035ec95dd2a Mon Sep 17 00:00:00 2001 From: Cohee Date: Sun, 4 Jun 2023 03:08:25 +0300 Subject: [PATCH] Add MS Edge TTS --- config.conf | 4 +- package-lock.json | 167 ++++++++++++++++++ package.json | 3 +- poe-test.js | 6 +- public/scripts/extensions/tts/edge.js | 124 +++++++++++++ public/scripts/extensions/tts/index.js | 42 +++++ public/scripts/extensions/tts/system.js | 18 +- server.js | 6 +- src/edge-tts.js | 49 +++++ poe-client.js => src/poe-client.js | 0 .../AddHumanMessageMutation.graphql | 0 .../AddMessageBreakMutation.graphql | 0 .../AutoSubscriptionMutation.graphql | 0 .../poe_graphql}/BioFragment.graphql | 0 .../ChatAddedSubscription.graphql | 0 .../poe_graphql}/ChatFragment.graphql | 0 .../ChatListPaginationQuery.graphql | 0 .../poe_graphql}/ChatPaginationQuery.graphql | 0 .../poe_graphql}/ChatViewQuery.graphql | 0 .../DeleteHumanMessagesMutation.graphql | 0 .../DeleteMessageMutation.graphql | 0 .../poe_graphql}/HandleFragment.graphql | 0 .../LoginWithVerificationCodeMutation.graphql | 0 .../MessageAddedSubscription.graphql | 0 .../MessageDeletedSubscription.graphql | 0 .../poe_graphql}/MessageFragment.graphql | 0 .../MessageRemoveVoteMutation.graphql | 0 .../MessageSetVoteMutation.graphql | 0 .../poe_graphql}/SendMessageMutation.graphql | 0 ...ndVerificationCodeForLoginMutation.graphql | 0 .../ShareMessagesMutation.graphql | 0 ...SignupWithVerificationCodeMutation.graphql | 0 .../StaleChatUpdateMutation.graphql | 0 .../SubscriptionsMutation.graphql | 0 .../SummarizePlainPostQuery.graphql | 0 .../SummarizeQuotePostQuery.graphql | 0 .../SummarizeSharePostQuery.graphql | 0 .../poe_graphql}/UserSnippetFragment.graphql | 0 .../poe_graphql}/ViewerInfoQuery.graphql | 0 ...werMessageLimitUpdatedSubscription.graphql | 0 .../poe_graphql}/ViewerStateFragment.graphql | 0 .../ViewerStateUpdatedSubscription.graphql | 0 42 files changed, 397 insertions(+), 22 deletions(-) create mode 100644 public/scripts/extensions/tts/edge.js create mode 100644 src/edge-tts.js rename poe-client.js => src/poe-client.js (100%) rename {poe_graphql => src/poe_graphql}/AddHumanMessageMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/AddMessageBreakMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/AutoSubscriptionMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/BioFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/ChatAddedSubscription.graphql (100%) rename {poe_graphql => src/poe_graphql}/ChatFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/ChatListPaginationQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/ChatPaginationQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/ChatViewQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/DeleteHumanMessagesMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/DeleteMessageMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/HandleFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/LoginWithVerificationCodeMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/MessageAddedSubscription.graphql (100%) rename {poe_graphql => src/poe_graphql}/MessageDeletedSubscription.graphql (100%) rename {poe_graphql => src/poe_graphql}/MessageFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/MessageRemoveVoteMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/MessageSetVoteMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/SendMessageMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/SendVerificationCodeForLoginMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/ShareMessagesMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/SignupWithVerificationCodeMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/StaleChatUpdateMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/SubscriptionsMutation.graphql (100%) rename {poe_graphql => src/poe_graphql}/SummarizePlainPostQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/SummarizeQuotePostQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/SummarizeSharePostQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/UserSnippetFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/ViewerInfoQuery.graphql (100%) rename {poe_graphql => src/poe_graphql}/ViewerMessageLimitUpdatedSubscription.graphql (100%) rename {poe_graphql => src/poe_graphql}/ViewerStateFragment.graphql (100%) rename {poe_graphql => src/poe_graphql}/ViewerStateUpdatedSubscription.graphql (100%) diff --git a/config.conf b/config.conf index b6f7dcb62..174f90294 100644 --- a/config.conf +++ b/config.conf @@ -1,5 +1,5 @@ -const port = 8000; +const port = 8100; const whitelist = ['127.0.0.1']; //Example for add several IP in whitelist: ['127.0.0.1', '192.168.0.10'] const whitelistMode = true; //Disabling enabling the ip whitelist mode. true/false const basicAuthMode = false; //Toggle basic authentication for endpoints. @@ -17,7 +17,7 @@ const securityOverride = false; module.exports = { port, - whitelist, + whitelist, whitelistMode, basicAuthMode, basicAuthUser, diff --git a/package-lock.json b/package-lock.json index 1ac24bc16..9e5fd68ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "json5": "^2.2.3", "lodash": "^4.17.21", "mime-types": "^2.1.35", + "msedge-tts": "^1.1.4", "multer": "^1.4.5-lts.1", "node-fetch": "^2.6.11", "node-rest-client": "^3.1.1", @@ -838,6 +839,18 @@ "version": "1.1.2", "license": "MIT" }, + "node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/busboy": { "version": "1.6.0", "dependencies": { @@ -1069,6 +1082,15 @@ "http-errors": "^2.0.0" } }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "node_modules/debug": { "version": "4.3.4", "license": "MIT", @@ -1194,6 +1216,39 @@ "once": "^1.4.0" } }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/escalade": { "version": "3.1.1", "license": "MIT", @@ -1283,6 +1338,19 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -1778,6 +1846,11 @@ "node": ">=0.12.0" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-wsl": { "version": "2.2.0", "license": "MIT", @@ -2006,6 +2079,23 @@ "version": "2.1.2", "license": "MIT" }, + "node_modules/msedge-tts": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/msedge-tts/-/msedge-tts-1.1.4.tgz", + "integrity": "sha512-0f7o3CqZJTpNHI4Ua4xr9Hf+GtjrC/KEVoNtJXfK3YLu/xC0/33q9LSlOdLVMZho3CFKboxOSpUQcI5nihR+Qg==", + "dependencies": { + "axios": "^0.21.1", + "websocket": "^1.0.34" + } + }, + "node_modules/msedge-tts/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/multer": { "version": "1.4.5-lts.1", "license": "MIT", @@ -2073,6 +2163,11 @@ "node": ">= 0.6" } }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-abi": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.40.0.tgz", @@ -2104,6 +2199,16 @@ } } }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-rest-client": { "version": "3.1.1", "license": "MIT", @@ -3171,6 +3276,11 @@ "node": "*" } }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "node_modules/type-is": { "version": "1.6.18", "license": "MIT", @@ -3186,6 +3296,14 @@ "version": "0.0.6", "license": "MIT" }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/uniqolor": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/uniqolor/-/uniqolor-1.1.0.tgz", @@ -3207,6 +3325,18 @@ "node": ">= 0.8" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/utf8-byte-length": { "version": "1.0.4", "license": "WTFPL" @@ -3245,6 +3375,35 @@ "version": "2.3.2", "license": "MIT" }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/whatwg-fetch": { "version": "3.6.2", "license": "MIT" @@ -3342,6 +3501,14 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 4730c703e..672eeb3fa 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "json5": "^2.2.3", "lodash": "^4.17.21", "mime-types": "^2.1.35", + "msedge-tts": "^1.1.4", "multer": "^1.4.5-lts.1", "node-fetch": "^2.6.11", "node-rest-client": "^3.1.1", @@ -69,7 +70,7 @@ ], "assets": [ "node_modules/**/*", - "poe_graphql/**/*" + "src/poe_graphql/**/*" ], "outputPath": "dist", "scripts": [ diff --git a/poe-test.js b/poe-test.js index b5e804c30..18aab9732 100644 --- a/poe-test.js +++ b/poe-test.js @@ -1,9 +1,9 @@ -const poe = require('./poe-client'); +const poe = require('./src/poe-client'); async function test() { const client = new poe.Client(); await client.init('pb-cookie'); - + const bots = client.get_bot_names(); console.log(bots); @@ -18,4 +18,4 @@ async function test() { client.disconnect_ws(); } -test(); \ No newline at end of file +test(); diff --git a/public/scripts/extensions/tts/edge.js b/public/scripts/extensions/tts/edge.js new file mode 100644 index 000000000..49dfcc7e7 --- /dev/null +++ b/public/scripts/extensions/tts/edge.js @@ -0,0 +1,124 @@ +import { getRequestHeaders } from "../../../script.js" +import { doExtrasFetch, getApiUrl, modules } from "../../extensions.js" +import { getPreviewString } from "./index.js" + +export { EdgeTtsProvider } + +class EdgeTtsProvider { + //########// + // Config // + //########// + + settings + voices = [] + separator = ' .. ' + + defaultSettings = { + voiceMap: {} + } + + get settingsHtml() { + let html = `Microsoft Edge TTS Provider
` + return html + } + + onSettingsChange() { + } + + loadSettings(settings) { + // Pupulate Provider UI given input settings + if (Object.keys(settings).length == 0) { + console.info("Using default TTS Provider settings") + } + + // Only accept keys defined in defaultSettings + this.settings = this.defaultSettings + + for (const key in settings){ + if (key in this.settings){ + this.settings[key] = settings[key] + } else { + throw `Invalid setting passed to TTS Provider: ${key}` + } + } + + console.info("Settings loaded") + } + + + async onApplyClick() { + return + } + + //#################// + // TTS Interfaces // + //#################// + + async getVoice(voiceName) { + if (this.voices.length == 0) { + this.voices = await this.fetchTtsVoiceIds() + } + const match = this.voices.filter( + voice => voice.name == voiceName + )[0] + if (!match) { + throw `TTS Voice name ${voiceName} not found` + } + return match + } + + async generateTts(text, voiceId){ + const response = await this.fetchTtsGeneration(text, voiceId) + return response + } + + //###########// + // API CALLS // + //###########// + async fetchTtsVoiceIds() { + const response = await doExtrasFetch(`/edge_voices`) + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${await response.json()}`) + } + let responseJson = await response.json() + responseJson = responseJson + .sort((a, b) => a.Locale.localeCompare(b.Locale) || a.ShortName.localeCompare(b.ShortName)) + .map(x => ({ name: x.ShortName, voice_id: x.ShortName, preview_url: false, lang: x.Locale })); + return responseJson + } + + + async previewTtsVoice(id) { + const voice = await this.getVoice(id); + const text = getPreviewString(voice.lang); + const response = await this.fetchTtsGeneration(text, id) + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${await response.json()}`) + } + + const audio = await response.blob(); + const url = URL.createObjectURL(audio); + const audioElement = document.createElement("audio"); + audioElement.src = url; + audioElement.play(); + } + + async fetchTtsGeneration(inputText, voiceId) { + console.info(`Generating new TTS for voice_id ${voiceId}`) + const response = await doExtrasFetch( + `/edge_speech`, + { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify({ + "text": inputText, + "voice": voiceId + }) + } + ) + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${await response.json()}`) + } + return response + } +} diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 2a75accfe..035d485c3 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -1,6 +1,7 @@ import { callPopup, cancelTtsPlay, eventSource, event_types, isMultigenEnabled, is_send_press, saveSettingsDebounced } from '../../../script.js' import { ModuleWorkerWrapper, extension_settings, getContext } from '../../extensions.js' import { getStringHash } from '../../utils.js' +import { EdgeTtsProvider } from './edge.js' import { ElevenLabsTtsProvider } from './elevenlabs.js' import { SileroTtsProvider } from './silerotts.js' import { SystemTtsProvider } from './system.js' @@ -15,11 +16,52 @@ let lastGroupId = null let lastChatId = null let lastMessageHash = null +export function getPreviewString(lang) { + const previewStrings = { + 'en-US': 'The quick brown fox jumps over the lazy dog', + 'en-GB': 'Sphinx of black quartz, judge my vow', + 'fr-FR': 'Portez ce vieux whisky au juge blond qui fume', + 'de-DE': 'Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich', + 'it-IT': "Pranzo d'acqua fa volti sghembi", + 'es-ES': 'Quiere la boca exhausta vid, kiwi, piña y fugaz jamón', + 'es-MX': 'Fabio me exige, sin tapujos, que añada cerveza al whisky', + 'ru-RU': 'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!', + 'pt-BR': 'Vejo xá gritando que fez show sem playback.', + 'pt-PR': 'Todo pajé vulgar faz boquinha sexy com kiwi.', + 'uk-UA': "Фабрикуймо гідність, лящім їжею, ґав хапаймо, з'єднавці чаш!", + 'pl-PL': 'Pchnąć w tę łódź jeża lub ośm skrzyń fig', + 'cs-CZ': 'Příliš žluťoučký kůň úpěl ďábelské ódy', + 'sk-SK': 'Vyhŕňme si rukávy a vyprážajme čínske ryžové cestoviny', + 'hu-HU': 'Árvíztűrő tükörfúrógép', + 'tr-TR': 'Pijamalı hasta yağız şoföre çabucak güvendi', + 'nl-NL': 'De waard heeft een kalfje en een pinkje opgegeten', + 'sv-SE': 'Yxskaftbud, ge vårbygd, zinkqvarn', + 'da-DK': 'Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Walther spillede på xylofon', + 'ja-JP': 'いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす', + 'ko-KR': '가나다라마바사아자차카타파하', + 'zh-CN': '我能吞下玻璃而不伤身体', + 'ro-RO': 'Muzicologă în bej vând whisky și tequila, preț fix', + 'bg-BG': 'Щъркелите се разпръснаха по цялото небе', + 'el-GR': 'Ταχίστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός', + 'fi-FI': 'Voi veljet, miksi juuri teille myin nämä vehkeet?', + 'he-IL': 'הקצינים צעקו: "כל הכבוד לצבא הצבאות!"', + 'id-ID': 'Jangkrik itu memang enak, apalagi kalau digoreng', + 'ms-MY': 'Muzik penyanyi wanita itu menggambarkan kehidupan yang penuh dengan duka nestapa', + 'th-TH': 'เป็นไงบ้างครับ ผมชอบกินข้าวผัดกระเพราหมูกรอบ', + 'vi-VN': 'Cô bé quàng khăn đỏ đang ngồi trên bãi cỏ xanh', + 'ar-SA': 'أَبْجَدِيَّة عَرَبِيَّة', + 'hi-IN': 'श्वेता ने श्वेता के श्वेते हाथों में श्वेता का श्वेता चावल पकड़ा', + } + const fallbackPreview = 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet' + + return previewStrings[lang] ?? fallbackPreview; +} let ttsProviders = { ElevenLabs: ElevenLabsTtsProvider, Silero: SileroTtsProvider, System: SystemTtsProvider, + Edge: EdgeTtsProvider, } let ttsProvider let ttsProviderName diff --git a/public/scripts/extensions/tts/system.js b/public/scripts/extensions/tts/system.js index b7431f1a6..74bd0aa51 100644 --- a/public/scripts/extensions/tts/system.js +++ b/public/scripts/extensions/tts/system.js @@ -1,3 +1,5 @@ +import { getPreviewString } from "./index.js"; + export { SystemTtsProvider } /** @@ -74,20 +76,6 @@ class SystemTtsProvider { // Config // //########// - previewStrings = { - 'en-US': 'The quick brown fox jumps over the lazy dog', - 'en-GB': 'Sphinx of black quartz, judge my vow', - 'fr-FR': 'Portez ce vieux whisky au juge blond qui fume', - 'de-DE': 'Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich', - 'it-IT': "Pranzo d'acqua fa volti sghembi", - 'es-ES': 'Quiere la boca exhausta vid, kiwi, piña y fugaz jamón', - 'es-MX': 'Fabio me exige, sin tapujos, que añada cerveza al whisky', - 'ru-RU': 'В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!', - 'pt-BR': 'Vejo xá gritando que fez show sem playback.', - 'pt-PR': 'Todo pajé vulgar faz boquinha sexy com kiwi.', - 'uk-UA': "Фабрикуймо гідність, лящім їжею, ґав хапаймо, з'єднавці чаш!", - } - fallbackPreview = 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet' settings voices = [] separator = ' ... ' @@ -172,7 +160,7 @@ class SystemTtsProvider { } speechSynthesis.cancel(); - const text = this.previewStrings[voice.lang] ?? this.fallbackPreview; + const text = getPreviewString(voice.lang); const utterance = new SpeechSynthesisUtterance(text); utterance.voice = voice; utterance.rate = 1; diff --git a/server.js b/server.js index 7ff6b95ec..eba69ff80 100644 --- a/server.js +++ b/server.js @@ -104,7 +104,7 @@ client.on('error', (err) => { console.error('An error occurred:', err); }); -const poe = require('./poe-client'); +const poe = require('./src/poe-client'); let api_server = "http://0.0.0.0:5000"; let api_novelai = "https://api.novelai.net"; @@ -3379,3 +3379,7 @@ async function getImageBuffers(zipFilePath) { }); }); } + +const edgeTts = require('./src/edge-tts'); + +edgeTts.addEdgeTtsEndpoints(app, jsonParser); diff --git a/src/edge-tts.js b/src/edge-tts.js new file mode 100644 index 000000000..dfbc2b015 --- /dev/null +++ b/src/edge-tts.js @@ -0,0 +1,49 @@ +const MsEdgeTTS = require('msedge-tts').MsEdgeTTS; + +const tts = new MsEdgeTTS(true); + +function getVoices() { + return tts.getVoices(); +} + +async function generateSpeech(text, voice) { + await tts.setMetadata(voice, MsEdgeTTS.OUTPUT_FORMAT.AUDIO_24KHZ_48KBITRATE_MONO_MP3); + + return new Promise((resolve, reject) => { + const readable = tts.toStream(text); + const chunks = []; + + readable.on("data", (data) => { + chunks.push(data); + }); + + readable.on("close", () => { + const buffer = Buffer.concat(chunks); + resolve(buffer); + }); + + readable.on("error", (err) => { + reject(err); + }); + }); +} + +function addEdgeTtsEndpoints(app, jsonParser) { + app.get("/edge_voices", jsonParser, async (req, res) => { + const voices = await getVoices(); + return res.send(voices); + }); + app.post("/edge_speech", jsonParser, async (req, res) => { + const { text, voice } = req.body; + + if (!text || !voice) { + return res.sendStatus(400); + } + + const buffer = await generateSpeech(text, voice); + res.set("Content-Type", "audio/mpeg"); + return res.send(buffer); + }); +} + +module.exports = { addEdgeTtsEndpoints }; diff --git a/poe-client.js b/src/poe-client.js similarity index 100% rename from poe-client.js rename to src/poe-client.js diff --git a/poe_graphql/AddHumanMessageMutation.graphql b/src/poe_graphql/AddHumanMessageMutation.graphql similarity index 100% rename from poe_graphql/AddHumanMessageMutation.graphql rename to src/poe_graphql/AddHumanMessageMutation.graphql diff --git a/poe_graphql/AddMessageBreakMutation.graphql b/src/poe_graphql/AddMessageBreakMutation.graphql similarity index 100% rename from poe_graphql/AddMessageBreakMutation.graphql rename to src/poe_graphql/AddMessageBreakMutation.graphql diff --git a/poe_graphql/AutoSubscriptionMutation.graphql b/src/poe_graphql/AutoSubscriptionMutation.graphql similarity index 100% rename from poe_graphql/AutoSubscriptionMutation.graphql rename to src/poe_graphql/AutoSubscriptionMutation.graphql diff --git a/poe_graphql/BioFragment.graphql b/src/poe_graphql/BioFragment.graphql similarity index 100% rename from poe_graphql/BioFragment.graphql rename to src/poe_graphql/BioFragment.graphql diff --git a/poe_graphql/ChatAddedSubscription.graphql b/src/poe_graphql/ChatAddedSubscription.graphql similarity index 100% rename from poe_graphql/ChatAddedSubscription.graphql rename to src/poe_graphql/ChatAddedSubscription.graphql diff --git a/poe_graphql/ChatFragment.graphql b/src/poe_graphql/ChatFragment.graphql similarity index 100% rename from poe_graphql/ChatFragment.graphql rename to src/poe_graphql/ChatFragment.graphql diff --git a/poe_graphql/ChatListPaginationQuery.graphql b/src/poe_graphql/ChatListPaginationQuery.graphql similarity index 100% rename from poe_graphql/ChatListPaginationQuery.graphql rename to src/poe_graphql/ChatListPaginationQuery.graphql diff --git a/poe_graphql/ChatPaginationQuery.graphql b/src/poe_graphql/ChatPaginationQuery.graphql similarity index 100% rename from poe_graphql/ChatPaginationQuery.graphql rename to src/poe_graphql/ChatPaginationQuery.graphql diff --git a/poe_graphql/ChatViewQuery.graphql b/src/poe_graphql/ChatViewQuery.graphql similarity index 100% rename from poe_graphql/ChatViewQuery.graphql rename to src/poe_graphql/ChatViewQuery.graphql diff --git a/poe_graphql/DeleteHumanMessagesMutation.graphql b/src/poe_graphql/DeleteHumanMessagesMutation.graphql similarity index 100% rename from poe_graphql/DeleteHumanMessagesMutation.graphql rename to src/poe_graphql/DeleteHumanMessagesMutation.graphql diff --git a/poe_graphql/DeleteMessageMutation.graphql b/src/poe_graphql/DeleteMessageMutation.graphql similarity index 100% rename from poe_graphql/DeleteMessageMutation.graphql rename to src/poe_graphql/DeleteMessageMutation.graphql diff --git a/poe_graphql/HandleFragment.graphql b/src/poe_graphql/HandleFragment.graphql similarity index 100% rename from poe_graphql/HandleFragment.graphql rename to src/poe_graphql/HandleFragment.graphql diff --git a/poe_graphql/LoginWithVerificationCodeMutation.graphql b/src/poe_graphql/LoginWithVerificationCodeMutation.graphql similarity index 100% rename from poe_graphql/LoginWithVerificationCodeMutation.graphql rename to src/poe_graphql/LoginWithVerificationCodeMutation.graphql diff --git a/poe_graphql/MessageAddedSubscription.graphql b/src/poe_graphql/MessageAddedSubscription.graphql similarity index 100% rename from poe_graphql/MessageAddedSubscription.graphql rename to src/poe_graphql/MessageAddedSubscription.graphql diff --git a/poe_graphql/MessageDeletedSubscription.graphql b/src/poe_graphql/MessageDeletedSubscription.graphql similarity index 100% rename from poe_graphql/MessageDeletedSubscription.graphql rename to src/poe_graphql/MessageDeletedSubscription.graphql diff --git a/poe_graphql/MessageFragment.graphql b/src/poe_graphql/MessageFragment.graphql similarity index 100% rename from poe_graphql/MessageFragment.graphql rename to src/poe_graphql/MessageFragment.graphql diff --git a/poe_graphql/MessageRemoveVoteMutation.graphql b/src/poe_graphql/MessageRemoveVoteMutation.graphql similarity index 100% rename from poe_graphql/MessageRemoveVoteMutation.graphql rename to src/poe_graphql/MessageRemoveVoteMutation.graphql diff --git a/poe_graphql/MessageSetVoteMutation.graphql b/src/poe_graphql/MessageSetVoteMutation.graphql similarity index 100% rename from poe_graphql/MessageSetVoteMutation.graphql rename to src/poe_graphql/MessageSetVoteMutation.graphql diff --git a/poe_graphql/SendMessageMutation.graphql b/src/poe_graphql/SendMessageMutation.graphql similarity index 100% rename from poe_graphql/SendMessageMutation.graphql rename to src/poe_graphql/SendMessageMutation.graphql diff --git a/poe_graphql/SendVerificationCodeForLoginMutation.graphql b/src/poe_graphql/SendVerificationCodeForLoginMutation.graphql similarity index 100% rename from poe_graphql/SendVerificationCodeForLoginMutation.graphql rename to src/poe_graphql/SendVerificationCodeForLoginMutation.graphql diff --git a/poe_graphql/ShareMessagesMutation.graphql b/src/poe_graphql/ShareMessagesMutation.graphql similarity index 100% rename from poe_graphql/ShareMessagesMutation.graphql rename to src/poe_graphql/ShareMessagesMutation.graphql diff --git a/poe_graphql/SignupWithVerificationCodeMutation.graphql b/src/poe_graphql/SignupWithVerificationCodeMutation.graphql similarity index 100% rename from poe_graphql/SignupWithVerificationCodeMutation.graphql rename to src/poe_graphql/SignupWithVerificationCodeMutation.graphql diff --git a/poe_graphql/StaleChatUpdateMutation.graphql b/src/poe_graphql/StaleChatUpdateMutation.graphql similarity index 100% rename from poe_graphql/StaleChatUpdateMutation.graphql rename to src/poe_graphql/StaleChatUpdateMutation.graphql diff --git a/poe_graphql/SubscriptionsMutation.graphql b/src/poe_graphql/SubscriptionsMutation.graphql similarity index 100% rename from poe_graphql/SubscriptionsMutation.graphql rename to src/poe_graphql/SubscriptionsMutation.graphql diff --git a/poe_graphql/SummarizePlainPostQuery.graphql b/src/poe_graphql/SummarizePlainPostQuery.graphql similarity index 100% rename from poe_graphql/SummarizePlainPostQuery.graphql rename to src/poe_graphql/SummarizePlainPostQuery.graphql diff --git a/poe_graphql/SummarizeQuotePostQuery.graphql b/src/poe_graphql/SummarizeQuotePostQuery.graphql similarity index 100% rename from poe_graphql/SummarizeQuotePostQuery.graphql rename to src/poe_graphql/SummarizeQuotePostQuery.graphql diff --git a/poe_graphql/SummarizeSharePostQuery.graphql b/src/poe_graphql/SummarizeSharePostQuery.graphql similarity index 100% rename from poe_graphql/SummarizeSharePostQuery.graphql rename to src/poe_graphql/SummarizeSharePostQuery.graphql diff --git a/poe_graphql/UserSnippetFragment.graphql b/src/poe_graphql/UserSnippetFragment.graphql similarity index 100% rename from poe_graphql/UserSnippetFragment.graphql rename to src/poe_graphql/UserSnippetFragment.graphql diff --git a/poe_graphql/ViewerInfoQuery.graphql b/src/poe_graphql/ViewerInfoQuery.graphql similarity index 100% rename from poe_graphql/ViewerInfoQuery.graphql rename to src/poe_graphql/ViewerInfoQuery.graphql diff --git a/poe_graphql/ViewerMessageLimitUpdatedSubscription.graphql b/src/poe_graphql/ViewerMessageLimitUpdatedSubscription.graphql similarity index 100% rename from poe_graphql/ViewerMessageLimitUpdatedSubscription.graphql rename to src/poe_graphql/ViewerMessageLimitUpdatedSubscription.graphql diff --git a/poe_graphql/ViewerStateFragment.graphql b/src/poe_graphql/ViewerStateFragment.graphql similarity index 100% rename from poe_graphql/ViewerStateFragment.graphql rename to src/poe_graphql/ViewerStateFragment.graphql diff --git a/poe_graphql/ViewerStateUpdatedSubscription.graphql b/src/poe_graphql/ViewerStateUpdatedSubscription.graphql similarity index 100% rename from poe_graphql/ViewerStateUpdatedSubscription.graphql rename to src/poe_graphql/ViewerStateUpdatedSubscription.graphql