Skip to content

Commit

Permalink
Merge pull request #678 from nature-heart-software/dev
Browse files Browse the repository at this point in the history
release
  • Loading branch information
Wurielle authored Dec 26, 2023
2 parents ced4747 + a79449e commit d3fc1de
Show file tree
Hide file tree
Showing 83 changed files with 1,141 additions and 277 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
npm-debug.log
yarn-error.log
2 changes: 2 additions & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
httpTimeout 600000
network-timeout 600000
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM mcr.microsoft.com/windows/servercore:ltsc2019

WORKDIR /app

RUN icacls "C:\app" /grant:r everyone:(OI)(CI)F /t

COPY ./scripts/install-nodejs.ps1 ./scripts/install-nodejs.ps1
RUN powershell -Command ./scripts/install-nodejs.ps1

COPY ./scripts/install-yarn.ps1 ./scripts/install-yarn.ps1
RUN powershell -Command ./scripts/install-yarn.ps1

COPY ./scripts/install-python.ps1 ./scripts/install-python.ps1
RUN powershell -Command ./scripts/install-python.ps1

COPY ./scripts/install-vs-build-tools.ps1 ./scripts/install-vs-build-tools.ps1
RUN powershell -Command ./scripts/install-vs-build-tools.ps1

ENV PATH="C:\nodejs;C:\Program Files (x86)\Yarn\bin;C:\Python310;C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64;${PATH}"

ENV PYTHON="C:\Python310\python.exe"

ENV NODE_OPTIONS="--max-old-space-size=4096"

RUN npm config set python C:\Python310\python.exe

RUN npm config set msvs_version 2022

COPY ./ ./

COPY ./lerna.json.prod ./lerna.json

RUN npm run build
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</h1>

<p align="center">
Your speech Assistant
Your speech assistant
</p>

<p align="center">
Expand Down Expand Up @@ -87,7 +87,7 @@ Here's a list of all the text-to-speech engines that are supported in Izabela:
| Izabela (multiple engines) | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/izabela-sample.mp3?raw=true) | https://github.com/Weilbyte/tiktok-tts |
| Amazon Polly | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/amazon-polly-sample.mp3?raw=true) | https://aws.amazon.com/polly/ |
| Google Cloud TTS | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/google-cloud-tts-sample.mp3?raw=true) | https://cloud.google.com/text-to-speech |
| IBM Watson TTS | Yes | | https://www.ibm.com/cloud/watson-text-to-speech |
| IBM Watson TTS | Temporarily unavailable | | https://www.ibm.com/cloud/watson-text-to-speech |
| Microsoft Azure TTS | Yes | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/microsoft-azure-tts-sample.mp3?raw=true) | https://azure.microsoft.com/en-us/products/cognitive-services/text-to-speech/ |
| Say | Included by default | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/say-sample.mp3?raw=true) | https://github.com/Marak/say.js/ |
| Sam | Included by default | [Download](https://github.com/nature-heart-software/izabela/blob/dev/assets/sam-sample.mp3?raw=true) | https://github.com/discordier/sam / https://github.com/Imrane03/better-sam |
Expand All @@ -101,3 +101,14 @@ Find guides and API documentation on the [Wiki](https://github.com/nature-heart-
## Resources

- [Figma](https://www.figma.com/proto/U4A6IwSY8T4W2tm2agW92S/Izabela-v1.0.0?node-id=103%3A4&scaling=min-zoom&page-id=103%3A3&starting-point-node-id=103%3A4)

## Development

The project requires the following in order to run:

- Node 16
- Yarn
- Python 3.10
- Visual Studio Build Tools 2019 or higher
- Desktop development with C++
- Node.js build tools
48 changes: 34 additions & 14 deletions apps/app-server/src/plugins/speech-apis/el.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { RequestHandler } from 'express'
import axios from 'axios'
import { handleError } from '../../utils/requests'
import hash from 'object-hash'

const plugin: Izabela.Server.Plugin = ({ app }) => {
const api = axios.create({
baseURL: 'https://api.elevenlabs.io/v1',
})
let settingsCache: string = ''
const listVoicesHandler: RequestHandler = async (
{
body: {
Expand All @@ -30,32 +28,53 @@ const plugin: Izabela.Server.Plugin = ({ app }) => {
}
}

const listModelsHandler: RequestHandler = async (
{
body: {
credentials: { apiKey },
},
},
res,
) => {
try {
const { data: models } = await api.get('/models', {
headers: {
'xi-api-key': apiKey,
},
})
res.status(200).json(models)
} catch (e: any) {
handleError(res, 'Internal server error', e.message, 500)
}
}

const synthesizeSpeechHandler: RequestHandler = async (
{
body: {
credentials: { apiKey },
payload: { text, voice, stability, similarity_boost },
payload: {
text,
voice,
stability,
similarity_boost,
use_speaker_boost,
style,
model_id,
},
},
},
res,
) => {
try {
const settings = {
const voice_settings = {
stability,
similarity_boost,
}
const hashedSettings = hash({ ...settings, voice_id: voice.voice_id })
if (settingsCache !== hashedSettings) {
await api.post(`/voices/${voice.voice_id}/settings/edit`, settings, {
headers: {
'xi-api-key': apiKey,
},
})
settingsCache = hashedSettings
use_speaker_boost,
style,
}
const { data } = await api.post(
`/text-to-speech/${voice.voice_id}/stream`,
{ text },
{ model_id, text, voice_settings },
{
headers: {
'xi-api-key': apiKey,
Expand All @@ -73,6 +92,7 @@ const plugin: Izabela.Server.Plugin = ({ app }) => {
}
}
app.post('/api/tts/elevenlabs/list-voices', listVoicesHandler)
app.post('/api/tts/elevenlabs/list-models', listModelsHandler)
app.post('/api/tts/elevenlabs/synthesize-speech', synthesizeSpeechHandler)
}

Expand Down
4 changes: 4 additions & 0 deletions apps/app/src/electron/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const App = () => {
}

const onQuit = () => {
/* fixes app.quit(): https://stackoverflow.com/a/75369483 */
ElectronWindowManager.getInstances().forEach(({ window }) => {
window.removeAllListeners()
})
destroyWinMouse()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ import { NvSelect } from '@packages/ui'
import { computed } from 'vue'
import { useSpeechEngineManager } from '@/modules/speech-engine-manager'
import { orderBy } from 'lodash'
import { groupOptions } from '@/utils/select'
import { capitalize } from '@/utils/text'
const { engines } = useSpeechEngineManager()
const options = computed(() =>
orderBy(
engines.value.map((engine) => {
const disabled = engine.hasCredentials ? !engine.hasCredentials() : false
return {
disabled,
label: engine.name,
value: engine.id,
attrs: {
title: disabled ? 'Requires credentials' : '',
},
}
}),
['disabled', 'label'],
['asc', 'asc'],
groupOptions(
orderBy(
engines.value.map((engine) => {
const disabled = engine.hasCredentials ? !engine.hasCredentials() : false
return {
disabled,
label: engine.name,
value: engine.id,
category: capitalize(engine.category),
attrs: {
title: disabled ? 'Requires credentials' : '',
},
}
}),
['disabled', 'label'],
['asc', 'asc'],
),
'category',
),
)
</script>
1 change: 1 addition & 0 deletions apps/app/src/modules/speech-engine-manager/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type Payload = { [key: string]: any }
export interface SpeechEngine {
id: string
name: string
category: 'cloud' | 'local' | 'other'
getVoiceName: (voice: any) => string
getSelectedVoice: () => any
getCredentials: () => Credentials
Expand Down
57 changes: 47 additions & 10 deletions apps/app/src/plugins/speech-engines/amazon-polly/NvVoiceSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,34 @@
...$attrs,
}"
valueKey="Id"
/>
>
<template #optionAfter="{ option, hover }">
<span v-show="(!option.children && hover) || favoriteVoiceIds.includes(option.id)">
<NvButton
:icon-name="favoriteVoiceIds.includes(option.id) ? 'times' : 'heart'"
:title="
favoriteVoiceIds.includes(option.id) ? 'Remove from favorites' : 'Add to favorites'
"
size="sm"
type="default"
@mousedown.prevent.stop="
setProperty('favoriteVoiceIds', xor(favoriteVoiceIds, [option.id]))
"
/>
</span>
</template>
</NvSelect>
</template>
<script lang="ts" setup>
import { computed, watch } from 'vue'
import { useQueryClient } from 'vue-query'
import { purify } from '@packages/toolbox'
import { orderBy } from 'lodash'
import { NvSelect } from '@packages/ui'
import { orderBy, xor } from 'lodash'
import { NvButton, NvSelect } from '@packages/ui'
import { useSpeechStore } from '@/features/speech/store'
import { groupOptions } from '@/utils/select'
import { useListVoicesQuery } from './hooks'
import { getVoiceName, LIST_VOICES_QUERY_KEY } from './shared'
import { getVoiceCategory, getVoiceId, getVoiceName, LIST_VOICES_QUERY_KEY } from './shared'
import { getProperty, setProperty } from './store'
const queryClient = useQueryClient()
Expand All @@ -40,12 +57,32 @@ const { data, isFetching } = useListVoicesQuery(computedParams, {
enabled: canFetch,
})
const voices = computed(() => orderBy(data.value || [], ['LanguageCode', 'Name']))
const options = computed(() =>
voices.value.map((voice) => ({
label: getVoiceName(voice),
value: voice,
})),
)
const getOptionFromVoice = (voice: any) => ({
id: getVoiceId(voice),
label: getVoiceName(voice),
value: voice,
category: getVoiceCategory(voice),
})
const options = computed(() => {
const localOptions = groupOptions(voices.value.map(getOptionFromVoice), 'category')
const favoriteVoiceIds = getProperty('favoriteVoiceIds')
if (favoriteVoiceIds) {
const favoriteVoices = voices.value.filter((voice: any) =>
favoriteVoiceIds.includes(getVoiceId(voice)),
)
if (favoriteVoices.length) {
localOptions.unshift({
label: 'Favorites',
children: favoriteVoices.map(getOptionFromVoice),
})
}
}
return localOptions
})
const favoriteVoiceIds = computed<string[]>(() => getProperty('favoriteVoiceIds'))
watch(
() => [getProperty('identityPoolId', true), getProperty('region')],
() => canFetch.value && queryClient.refetchQueries(LIST_VOICES_QUERY_KEY),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const getSelectedVoice = () => getProperty('selectedVoice')
registerEngine({
id: ENGINE_ID,
name: ENGINE_NAME,
category: 'cloud',
getSelectedVoice,
getVoiceName,
getCredentials,
Expand Down
2 changes: 2 additions & 0 deletions apps/app/src/plugins/speech-engines/amazon-polly/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export const ENGINE_ID = 'aptts' as const
export const ENGINE_NAME = 'Amazon Polly' as const
export const LIST_VOICES_QUERY_KEY = 'aptts-list-voices' as const
export const getVoiceName = (voice: any) => `${voice.LanguageCode} ${voice.Name} - ${voice.Gender}`
export const getVoiceId = (voice: any) => voice.Id
export const getVoiceCategory = (voice: any) => voice.LanguageName
1 change: 1 addition & 0 deletions apps/app/src/plugins/speech-engines/amazon-polly/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export const { setProperty, getProperty } = definePluginStore(ENGINE_ID, {
Name: 'Amy',
SupportedEngines: ['neural', 'standard'],
},
favoriteVoiceIds: [],
useLocalCredentials: false,
})
Loading

0 comments on commit d3fc1de

Please sign in to comment.