mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-01-25 02:08:19 +00:00
Compare commits
5 Commits
feat/fix_c
...
fix/filter
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8adb8dbe0 | ||
|
|
3a19ff6840 | ||
|
|
729cefb06c | ||
|
|
26e3856603 | ||
|
|
f5302583b5 |
45
package-lock.json
generated
45
package-lock.json
generated
@@ -21,7 +21,7 @@
|
||||
"react-dom": "^18.0.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"wavesurfer.js": "^6.6.3"
|
||||
"wavesurfer.js": "^7.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
@@ -30,7 +30,6 @@
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/wavesurfer.js": "^6.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
@@ -900,12 +899,6 @@
|
||||
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||
@@ -1014,15 +1007,6 @@
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/wavesurfer.js": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/wavesurfer.js/-/wavesurfer.js-6.0.6.tgz",
|
||||
"integrity": "sha512-fD54o0RXZXxkOb+69Rt6rGViaHpIc1Mmde2aOX9qPhlQhrCPepybGnsekiG407+7scPlaK+hmuPez5AnnmlzGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/debounce": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
|
||||
@@ -6867,9 +6851,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wavesurfer.js": {
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-6.6.3.tgz",
|
||||
"integrity": "sha512-XqUOXe8+SOTe8uKCHaqW0vJ5etCCQvq/NgaPycn9HAX/nUi+2zoWD+w9i7H5vBT9UCDNawOia+vS5Ct3kZGQzA=="
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.3.4.tgz",
|
||||
"integrity": "sha512-x2Ue4Dh+4RoaWay3LOLHhXgkdxPAIoC/BcbXh0yk8WNhQH2NboPoa52XXoCo4jEfjSe1bc7nxuM5vBIxUMZyBA=="
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
@@ -7678,12 +7662,6 @@
|
||||
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||
@@ -7787,15 +7765,6 @@
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/wavesurfer.js": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/wavesurfer.js/-/wavesurfer.js-6.0.6.tgz",
|
||||
"integrity": "sha512-fD54o0RXZXxkOb+69Rt6rGViaHpIc1Mmde2aOX9qPhlQhrCPepybGnsekiG407+7scPlaK+hmuPez5AnnmlzGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/debounce": "*"
|
||||
}
|
||||
},
|
||||
"@types/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
|
||||
@@ -11657,9 +11626,9 @@
|
||||
}
|
||||
},
|
||||
"wavesurfer.js": {
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-6.6.3.tgz",
|
||||
"integrity": "sha512-XqUOXe8+SOTe8uKCHaqW0vJ5etCCQvq/NgaPycn9HAX/nUi+2zoWD+w9i7H5vBT9UCDNawOia+vS5Ct3kZGQzA=="
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.3.4.tgz",
|
||||
"integrity": "sha512-x2Ue4Dh+4RoaWay3LOLHhXgkdxPAIoC/BcbXh0yk8WNhQH2NboPoa52XXoCo4jEfjSe1bc7nxuM5vBIxUMZyBA=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/ui-kit": "^0.0.21",
|
||||
"@stripe/react-stripe-js": "^2.1.1",
|
||||
"@stripe/stripe-js": "^1.54.1",
|
||||
"dayjs": "^1.11.5",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"react": "^18.0.0",
|
||||
@@ -50,9 +52,7 @@
|
||||
"react-dom": "^18.0.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"wavesurfer.js": "^6.6.3",
|
||||
"@stripe/react-stripe-js": "^2.1.1",
|
||||
"@stripe/stripe-js": "^1.54.1"
|
||||
"wavesurfer.js": "^7.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
@@ -61,7 +61,6 @@
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/wavesurfer.js": "^6.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
|
||||
@@ -197,6 +197,14 @@ export const AUDIO_FORMAT_OPTIONS = [
|
||||
value: "wav",
|
||||
},
|
||||
];
|
||||
|
||||
export const DEFAULT_ELEVENLABS_MODEL = "eleven_multilingual_v2";
|
||||
|
||||
export const ELEVENLABS_MODEL_OPTIONS = [
|
||||
{ name: "Multilingual v2", value: "eleven_multilingual_v2" },
|
||||
{ name: "Multilingual v1", value: "eleven_multilingual_v1" },
|
||||
{ name: "English v1", value: "eleven_monolingual_v1" },
|
||||
];
|
||||
/** Password Length options */
|
||||
|
||||
export const PASSWORD_MIN = 8;
|
||||
|
||||
@@ -90,6 +90,10 @@ import type {
|
||||
DeleteAccount,
|
||||
ChangePassword,
|
||||
SignIn,
|
||||
GetVoices,
|
||||
LanguageOption,
|
||||
VoiceOption,
|
||||
GetLanguages,
|
||||
} from "./types";
|
||||
import { Availability, StatusCodes } from "./types";
|
||||
import { JaegerRoot } from "./jaeger-types";
|
||||
@@ -329,6 +333,32 @@ export const postSpeechService = (
|
||||
return postFetch<SidResponse, Partial<SpeechCredential>>(apiUrl, payload);
|
||||
};
|
||||
|
||||
export const postSpeechServiceVoices = (
|
||||
sid: string,
|
||||
payload: Partial<GetVoices>
|
||||
) => {
|
||||
const userData = parseJwt(getToken());
|
||||
const apiUrl =
|
||||
userData.scope === USER_ACCOUNT
|
||||
? `${API_ACCOUNTS}/${userData.account_sid}/SpeechCredentials/voices`
|
||||
: `${API_SERVICE_PROVIDERS}/${sid}/SpeechCredentials/voices`;
|
||||
|
||||
return postFetch<VoiceOption[], Partial<GetVoices>>(apiUrl, payload);
|
||||
};
|
||||
|
||||
export const postSpeechServiceLanguages = (
|
||||
sid: string,
|
||||
payload: Partial<GetLanguages>
|
||||
) => {
|
||||
const userData = parseJwt(getToken());
|
||||
const apiUrl =
|
||||
userData.scope === USER_ACCOUNT
|
||||
? `${API_ACCOUNTS}/${userData.account_sid}/SpeechCredentials/languages`
|
||||
: `${API_SERVICE_PROVIDERS}/${sid}/SpeechCredentials/languages`;
|
||||
|
||||
return postFetch<LanguageOption[], Partial<GetLanguages>>(apiUrl, payload);
|
||||
};
|
||||
|
||||
export const postMsTeamsTentant = (payload: Partial<MSTeamsTenant>) => {
|
||||
return postFetch<SidResponse, Partial<MSTeamsTenant>>(
|
||||
API_MS_TEAMS_TENANTS,
|
||||
|
||||
@@ -37,14 +37,14 @@ export interface JaegerAttribute {
|
||||
value: JaegerValue;
|
||||
}
|
||||
|
||||
export interface WaveSufferSttResult {
|
||||
export interface WaveSurferSttResult {
|
||||
vendor: string;
|
||||
transcript: string;
|
||||
confidence: number;
|
||||
language_code: string;
|
||||
}
|
||||
|
||||
export interface WaveSufferDtmfResult {
|
||||
export interface WaveSurferDtmfResult {
|
||||
dtmf: string;
|
||||
duration: string;
|
||||
}
|
||||
|
||||
@@ -407,6 +407,7 @@ export interface SpeechCredential {
|
||||
custom_tts_url: null | string;
|
||||
label: null | string;
|
||||
cobalt_server_uri: null | string;
|
||||
model_id: null | string;
|
||||
}
|
||||
|
||||
export interface Alert {
|
||||
@@ -675,3 +676,20 @@ export interface SignIn {
|
||||
jwt?: null | string;
|
||||
account_sid?: null | string;
|
||||
}
|
||||
|
||||
export interface GetVoices {
|
||||
vendor: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface VoiceOption extends SelectorOptions {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface GetLanguages extends GetVoices {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface LanguageOption extends SelectorOptions {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export const Accounts = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter accounts"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -96,7 +96,7 @@ export const Applications = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter applications"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { postSpeechServiceLanguages, postSpeechServiceVoices } from "src/api";
|
||||
import { SpeechCredential } from "src/api/types";
|
||||
import { Selector } from "src/components/forms";
|
||||
import { SelectorOption } from "src/components/forms/selector";
|
||||
import { useSelectState } from "src/store";
|
||||
import { hasLength } from "src/utils";
|
||||
import {
|
||||
ELEVENLABS_LANG_EN,
|
||||
LANG_COBALT_EN_US,
|
||||
LANG_EN_US,
|
||||
LANG_EN_US_STANDARD_C,
|
||||
@@ -10,6 +14,7 @@ import {
|
||||
VENDOR_COBALT,
|
||||
VENDOR_CUSTOM,
|
||||
VENDOR_DEEPGRAM,
|
||||
VENDOR_ELEVENLABS,
|
||||
VENDOR_GOOGLE,
|
||||
VENDOR_MICROSOFT,
|
||||
VENDOR_SONIOX,
|
||||
@@ -66,6 +71,73 @@ export const SpeechProviderSelection = ({
|
||||
const [selectedCredential, setSelectedCredential] = useState<
|
||||
SpeechCredential | undefined
|
||||
>();
|
||||
const [synthesisVoiceOptions, setSynthesisVoiceOptions] = useState<
|
||||
SelectorOption[]
|
||||
>([]);
|
||||
const [synthesisLanguageOptions, setSynthesisLanguageOptions] = useState<
|
||||
SelectorOption[]
|
||||
>([]);
|
||||
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
useEffect(() => {
|
||||
if (!synthesis) {
|
||||
return;
|
||||
}
|
||||
let options = synthesis[synthVendor as keyof SynthesisVendors]
|
||||
.filter((lang: VoiceLanguage) => {
|
||||
// ELEVENLABS has same voice for all lange, take voices from the 1st language
|
||||
if (synthVendor === VENDOR_ELEVENLABS) {
|
||||
return true;
|
||||
}
|
||||
return lang.code === synthLang;
|
||||
})
|
||||
.flatMap((lang: VoiceLanguage) =>
|
||||
lang.voices.map((voice: Voice) => ({
|
||||
name: voice.name,
|
||||
value: voice.value,
|
||||
}))
|
||||
) as Voice[];
|
||||
setSynthesisVoiceOptions(options);
|
||||
|
||||
options = synthesis[synthVendor as keyof SynthesisVendors].map(
|
||||
(lang: VoiceLanguage) => ({
|
||||
name: lang.name,
|
||||
value: lang.code,
|
||||
})
|
||||
);
|
||||
setSynthesisLanguageOptions(options);
|
||||
|
||||
if (synthVendor === VENDOR_ELEVENLABS) {
|
||||
postSpeechServiceVoices(
|
||||
currentServiceProvider
|
||||
? currentServiceProvider.service_provider_sid
|
||||
: "",
|
||||
{
|
||||
vendor: synthVendor,
|
||||
label: synthLabel,
|
||||
}
|
||||
).then(({ json }) => {
|
||||
if (json.length > 0) {
|
||||
setSynthesisVoiceOptions(json);
|
||||
}
|
||||
});
|
||||
|
||||
postSpeechServiceLanguages(
|
||||
currentServiceProvider
|
||||
? currentServiceProvider.service_provider_sid
|
||||
: "",
|
||||
{
|
||||
vendor: synthVendor,
|
||||
label: synthLabel,
|
||||
}
|
||||
).then(({ json }) => {
|
||||
if (json.length > 0) {
|
||||
setSynthesisLanguageOptions(json);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [synthVendor, synthesis, synthLabel]);
|
||||
|
||||
useEffect(() => {
|
||||
if (credentials) {
|
||||
@@ -112,6 +184,15 @@ export const SpeechProviderSelection = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (vendor === VENDOR_ELEVENLABS) {
|
||||
const newLang = synthesis[vendor].find(
|
||||
(lang) => lang.code === ELEVENLABS_LANG_EN
|
||||
);
|
||||
setSynthLang(ELEVENLABS_LANG_EN);
|
||||
setSynthVoice(newLang!.voices[0].value);
|
||||
return;
|
||||
}
|
||||
|
||||
/** Google and AWS have different language lists */
|
||||
/** If the new language doesn't map then default to "en-US" */
|
||||
let newLang = synthesis[vendor].find(
|
||||
@@ -154,12 +235,7 @@ export const SpeechProviderSelection = ({
|
||||
id="synthesis_lang"
|
||||
name="synthesis_lang"
|
||||
value={synthLang}
|
||||
options={synthesis[synthVendor as keyof SynthesisVendors].map(
|
||||
(lang: VoiceLanguage) => ({
|
||||
name: lang.name,
|
||||
value: lang.code,
|
||||
})
|
||||
)}
|
||||
options={synthesisLanguageOptions}
|
||||
onChange={(e) => {
|
||||
const language = e.target.value;
|
||||
setSynthLang(language);
|
||||
@@ -200,18 +276,7 @@ export const SpeechProviderSelection = ({
|
||||
id="synthesis_voice"
|
||||
name="synthesis_voice"
|
||||
value={synthVoice}
|
||||
options={
|
||||
synthesis[synthVendor as keyof SynthesisVendors]
|
||||
.filter(
|
||||
(lang: VoiceLanguage) => lang.code === synthLang
|
||||
)
|
||||
.flatMap((lang: VoiceLanguage) =>
|
||||
lang.voices.map((voice: Voice) => ({
|
||||
name: voice.name,
|
||||
value: voice.value,
|
||||
}))
|
||||
) as Voice[]
|
||||
}
|
||||
options={synthesisVoiceOptions}
|
||||
onChange={(e) => setSynthVoice(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -155,7 +155,7 @@ export const Carriers = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter carriers"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -126,7 +126,7 @@ export const Clients = () => {
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter clients"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -99,7 +99,7 @@ export const Lcrs = () => {
|
||||
multiple carriers available.
|
||||
</M>
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter placeholder="Filter lcrs" filter={[filter, setFilter]} />
|
||||
<ScopedAccess user={user} scope={Scope.admin}>
|
||||
<AccountFilter
|
||||
|
||||
@@ -89,7 +89,7 @@ export const MSTeamsTenants = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter ms teams tenants"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -129,7 +129,7 @@ export const PhoneNumbers = () => {
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
<section className="filters filters--spaced">
|
||||
<section className="filters filters--multi">
|
||||
<SearchFilter
|
||||
placeholder="Filter phone numbers"
|
||||
filter={[filter, setFilter]}
|
||||
|
||||
@@ -6,14 +6,14 @@ import { Icon, P } from "@jambonz/ui-kit";
|
||||
import { Icons, Modal, ModalClose } from "src/components";
|
||||
import { deleteRecord, getBlob, getJaegerTrace } from "src/api";
|
||||
import { DownloadedBlob, RecentCall } from "src/api/types";
|
||||
import RegionsPlugin, { Region } from "wavesurfer.js/src/plugin/regions";
|
||||
import TimelinePlugin from "wavesurfer.js/src/plugin/timeline";
|
||||
import RegionsPlugin, { Region } from "wavesurfer.js/dist/plugins/regions";
|
||||
import TimelinePlugin from "wavesurfer.js/dist/plugins/timeline";
|
||||
import { API_BASE_URL } from "src/api/constants";
|
||||
import {
|
||||
JaegerRoot,
|
||||
JaegerSpan,
|
||||
WaveSufferDtmfResult,
|
||||
WaveSufferSttResult,
|
||||
WaveSurferDtmfResult,
|
||||
WaveSurferSttResult,
|
||||
} from "src/api/jaeger-types";
|
||||
import {
|
||||
getSpanAttributeByName,
|
||||
@@ -38,23 +38,26 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [playBackTime, setPlayBackTime] = useState("");
|
||||
const [jaegerRoot, setJeagerRoot] = useState<JaegerRoot>();
|
||||
const [waveSufferRegionData, setWaveSufferRegionData] =
|
||||
useState<WaveSufferSttResult | null>();
|
||||
const [waveSufferDtmfData, setWaveSufferDtmfData] =
|
||||
useState<WaveSufferDtmfResult | null>();
|
||||
const [waveSurferRegionData, setWaveSurferRegionData] =
|
||||
useState<WaveSurferSttResult | null>();
|
||||
const [waveSurferDtmfData, setWaveSurferDtmfData] =
|
||||
useState<WaveSurferDtmfResult | null>();
|
||||
const [regionChecked, setRegionChecked] = useState(false);
|
||||
|
||||
const wavesurferId = `wavesurfer--${call_sid}`;
|
||||
const wavesurferTimelineId = `timeline-${wavesurferId}`;
|
||||
const waveSufferRef = useRef<WaveSurfer | null>(null);
|
||||
const waveSurferRef = useRef<WaveSurfer | null>(null);
|
||||
const waveSurferRegionsPluginRef = useRef<RegionsPlugin | null>();
|
||||
|
||||
const [record, setRecord] = useState<DownloadedBlob | null>(null);
|
||||
|
||||
const [deleteRecordUrl, setDeleteRecordUrl] = useState("");
|
||||
|
||||
const drawDtmfRegionForSpan = (s: JaegerSpan, startPoint: JaegerSpan) => {
|
||||
if (waveSufferRef.current) {
|
||||
const r = waveSufferRef.current.regions.list[s.spanId];
|
||||
if (waveSurferRegionsPluginRef.current) {
|
||||
waveSurferRef.current;
|
||||
const r = waveSurferRegionsPluginRef.current
|
||||
.getRegions()
|
||||
.find((r) => r.id === s.spanId);
|
||||
if (!r) {
|
||||
const [dtmfValue] = getSpanAttributeByName(s.attributes, "dtmf");
|
||||
const [durationValue] = getSpanAttributeByName(
|
||||
@@ -67,29 +70,28 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
1_000_000_000;
|
||||
const duration =
|
||||
Number(durationValue.value.stringValue.replace("ms", "")) / 1_000;
|
||||
// as duration of DTMF is short, cannot be shown in wavesuffer,
|
||||
// as duration of DTMF is short, cannot be shown in wavesurfer,
|
||||
// adjust region width here.
|
||||
const delta = duration <= 0.1 ? 0.1 : duration;
|
||||
const end = start + delta;
|
||||
|
||||
const region = waveSufferRef.current.addRegion({
|
||||
const region = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId,
|
||||
start,
|
||||
end,
|
||||
color: "rgba(138, 43, 226, 0.15)",
|
||||
drag: false,
|
||||
loop: false,
|
||||
resize: false,
|
||||
});
|
||||
changeRegionMouseStyle(region);
|
||||
|
||||
const att: WaveSufferDtmfResult = {
|
||||
const att: WaveSurferDtmfResult = {
|
||||
dtmf: dtmfValue.value.stringValue,
|
||||
duration: durationValue.value.stringValue,
|
||||
};
|
||||
|
||||
region.on("click", () => {
|
||||
setWaveSufferDtmfData(att);
|
||||
setWaveSurferDtmfData(att);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -115,8 +117,10 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
startPoint: JaegerSpan,
|
||||
channel = 0
|
||||
) => {
|
||||
if (waveSufferRef.current) {
|
||||
const r = waveSufferRef.current.regions.list[s.spanId];
|
||||
if (waveSurferRegionsPluginRef.current) {
|
||||
const r = waveSurferRegionsPluginRef.current
|
||||
.getRegions()
|
||||
.find((r) => r.id === s.spanId);
|
||||
if (!r) {
|
||||
const start =
|
||||
(s.startTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000 +
|
||||
@@ -124,18 +128,18 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
const end =
|
||||
(s.endTimeUnixNano - startPoint.startTimeUnixNano) / 1_000_000_000;
|
||||
|
||||
const region = waveSufferRef.current.addRegion({
|
||||
const region = waveSurferRegionsPluginRef.current.addRegion({
|
||||
id: s.spanId,
|
||||
start,
|
||||
end,
|
||||
color: "rgba(255, 0, 0, 0.15)",
|
||||
drag: false,
|
||||
loop: false,
|
||||
resize: false,
|
||||
});
|
||||
|
||||
changeRegionMouseStyle(region, channel);
|
||||
const [sttResult] = getSpanAttributeByName(s.attributes, "stt.result");
|
||||
let att: WaveSufferSttResult;
|
||||
let att: WaveSurferSttResult;
|
||||
if (sttResult) {
|
||||
const data = JSON.parse(sttResult.value.stringValue);
|
||||
att = {
|
||||
@@ -168,13 +172,13 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
}
|
||||
|
||||
region.on("click", () => {
|
||||
setWaveSufferRegionData(att);
|
||||
setWaveSurferRegionData(att);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildWavesufferRegion = () => {
|
||||
const buildWavesurferRegion = () => {
|
||||
if (jaegerRoot) {
|
||||
const spans = getSpansFromJaegerRoot(jaegerRoot);
|
||||
const [startPoint] = getSpansByName(spans, "background-listen:listen");
|
||||
@@ -218,7 +222,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
buildWavesufferRegion();
|
||||
buildWavesurferRegion();
|
||||
}, [jaegerRoot, isReady]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -251,8 +255,9 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (waveSufferRef.current !== null || !record) return;
|
||||
waveSufferRef.current = WaveSurfer.create({
|
||||
if (waveSurferRef.current !== null || !record) return;
|
||||
waveSurferRegionsPluginRef.current = RegionsPlugin.create();
|
||||
waveSurferRef.current = WaveSurfer.create({
|
||||
container: `#${wavesurferId}`,
|
||||
waveColor: "#da1c5c",
|
||||
progressColor: "grey",
|
||||
@@ -260,62 +265,68 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
cursorWidth: 1,
|
||||
cursorColor: "lightgray",
|
||||
normalize: true,
|
||||
responsive: true,
|
||||
fillParent: true,
|
||||
splitChannels: true,
|
||||
scrollParent: true,
|
||||
autoScroll: true,
|
||||
splitChannels: [],
|
||||
minPxPerSec: 100,
|
||||
plugins: [
|
||||
RegionsPlugin.create({}),
|
||||
waveSurferRegionsPluginRef.current,
|
||||
TimelinePlugin.create({
|
||||
container: `#${wavesurferTimelineId}`,
|
||||
timeInterval: 0.2,
|
||||
primaryLabelInterval: 5,
|
||||
secondaryLabelInterval: 1,
|
||||
style: {
|
||||
fontSize: "15px",
|
||||
color: "#000000",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
waveSufferRef.current.load(record?.data_url);
|
||||
waveSurferRef.current.load(record?.data_url);
|
||||
// All event should be after load
|
||||
waveSufferRef.current.on("finish", () => {
|
||||
waveSurferRef.current.on("finish", () => {
|
||||
setIsPlaying(false);
|
||||
});
|
||||
|
||||
waveSufferRef.current.on("play", () => {
|
||||
waveSurferRef.current.on("play", () => {
|
||||
setIsPlaying(true);
|
||||
});
|
||||
|
||||
waveSufferRef.current.on("pause", () => {
|
||||
waveSurferRef.current.on("pause", () => {
|
||||
setIsPlaying(false);
|
||||
});
|
||||
|
||||
waveSufferRef.current.on("ready", () => {
|
||||
waveSurferRef.current.on("ready", () => {
|
||||
setIsReady(true);
|
||||
setPlayBackTime(formatTime(waveSufferRef.current?.getDuration() || 0));
|
||||
setPlayBackTime(formatTime(waveSurferRef.current?.getDuration() || 0));
|
||||
});
|
||||
|
||||
waveSufferRef.current.on("audioprocess", () => {
|
||||
setPlayBackTime(formatTime(waveSufferRef.current?.getCurrentTime() || 0));
|
||||
waveSurferRef.current.on("audioprocess", () => {
|
||||
setPlayBackTime(formatTime(waveSurferRef.current?.getCurrentTime() || 0));
|
||||
});
|
||||
}, [record]);
|
||||
|
||||
const togglePlayback = () => {
|
||||
if (waveSufferRef.current) {
|
||||
if (waveSurferRef.current) {
|
||||
if (!isPlaying) {
|
||||
waveSufferRef.current.play();
|
||||
waveSurferRef.current.play();
|
||||
} else {
|
||||
waveSufferRef.current.pause();
|
||||
waveSurferRef.current.pause();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setPlaybackJump = (delta: number) => {
|
||||
if (waveSufferRef.current) {
|
||||
const idx = waveSufferRef.current.getCurrentTime() + delta;
|
||||
if (waveSurferRef.current) {
|
||||
const idx = waveSurferRef.current.getCurrentTime() + delta;
|
||||
const value =
|
||||
idx <= 0
|
||||
? 0
|
||||
: idx >= waveSufferRef.current.getDuration()
|
||||
? waveSufferRef.current.getDuration() - 1
|
||||
: idx >= waveSurferRef.current.getDuration()
|
||||
? waveSurferRef.current.getDuration() - 1
|
||||
: idx;
|
||||
waveSufferRef.current.setCurrentTime(value);
|
||||
waveSurferRef.current.setTime(value);
|
||||
setPlayBackTime(formatTime(value));
|
||||
}
|
||||
};
|
||||
@@ -326,7 +337,6 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
<>
|
||||
<div className="media-container">
|
||||
<div id={wavesurferId} />
|
||||
<div id={wavesurferTimelineId} />
|
||||
<div className="media-container__center">
|
||||
<strong>{playBackTime}</strong>
|
||||
</div>
|
||||
@@ -404,8 +414,9 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
checked={regionChecked}
|
||||
onChange={(e) => {
|
||||
setRegionChecked(e.target.checked);
|
||||
if (waveSufferRef.current) {
|
||||
const regionsList = waveSufferRef.current.regions.list;
|
||||
if (waveSurferRegionsPluginRef.current) {
|
||||
const regionsList =
|
||||
waveSurferRegionsPluginRef.current.getRegions();
|
||||
for (const [, region] of Object.entries(regionsList)) {
|
||||
region.element.style.display = e.target.checked ? "" : "none";
|
||||
}
|
||||
@@ -415,8 +426,8 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
<div>Overlay STT and DTMF events</div>
|
||||
</label>
|
||||
</div>
|
||||
{waveSufferRegionData && (
|
||||
<ModalClose handleClose={() => setWaveSufferRegionData(null)}>
|
||||
{waveSurferRegionData && (
|
||||
<ModalClose handleClose={() => setWaveSurferRegionData(null)}>
|
||||
<div className="spanDetailsWrapper__header">
|
||||
<P>
|
||||
<strong>Speech to text result</strong>
|
||||
@@ -424,43 +435,43 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
</div>
|
||||
<div className="spanDetailsWrapper">
|
||||
<div className="spanDetailsWrapper__detailsWrapper">
|
||||
{waveSufferRegionData.vendor && (
|
||||
{waveSurferRegionData.vendor && (
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Vendor:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferRegionData.vendor}
|
||||
{waveSurferRegionData.vendor}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{waveSufferRegionData.confidence !== 0 && (
|
||||
{waveSurferRegionData.confidence !== 0 && (
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Confidence:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferRegionData.confidence}
|
||||
{waveSurferRegionData.confidence}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{waveSufferRegionData.language_code && (
|
||||
{waveSurferRegionData.language_code && (
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Language code:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferRegionData.language_code}
|
||||
{waveSurferRegionData.language_code}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{waveSufferRegionData.transcript && (
|
||||
{waveSurferRegionData.transcript && (
|
||||
<div className="spanDetailsWrapper__details">
|
||||
<div className="spanDetailsWrapper__details_header">
|
||||
<strong>Transcript:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferRegionData.transcript}
|
||||
{waveSurferRegionData.transcript}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -468,8 +479,8 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
</div>
|
||||
</ModalClose>
|
||||
)}
|
||||
{waveSufferDtmfData && (
|
||||
<ModalClose handleClose={() => setWaveSufferDtmfData(null)}>
|
||||
{waveSurferDtmfData && (
|
||||
<ModalClose handleClose={() => setWaveSurferDtmfData(null)}>
|
||||
<div className="spanDetailsWrapper__header">
|
||||
<P>
|
||||
<strong>Dtmf result</strong>
|
||||
@@ -482,7 +493,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
<strong>Dtmf:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferDtmfData.dtmf}
|
||||
{waveSurferDtmfData.dtmf}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -491,7 +502,7 @@ export const Player = ({ call }: PlayerProps) => {
|
||||
<strong>Duration:</strong>
|
||||
</div>
|
||||
<div className="spanDetailsWrapper__details_body">
|
||||
{waveSufferDtmfData.duration}
|
||||
{waveSurferDtmfData.duration}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
}
|
||||
|
||||
.media-container {
|
||||
overflow-x: auto;
|
||||
border: 1px solid black;
|
||||
border-radius: ui-vars.$px01;
|
||||
padding: 13px;
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
VENDOR_SONIOX,
|
||||
VENDOR_CUSTOM,
|
||||
VENDOR_COBALT,
|
||||
VENDOR_ELEVENLABS,
|
||||
} from "src/vendor";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import {
|
||||
@@ -45,7 +46,11 @@ import { CredentialStatus } from "./status";
|
||||
import type { RegionVendors, GoogleServiceKey, Vendor } from "src/vendor/types";
|
||||
import type { Account, SpeechCredential, UseApiDataMap } from "src/api/types";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { DISABLE_CUSTOM_SPEECH } from "src/api/constants";
|
||||
import {
|
||||
DEFAULT_ELEVENLABS_MODEL,
|
||||
DISABLE_CUSTOM_SPEECH,
|
||||
ELEVENLABS_MODEL_OPTIONS,
|
||||
} from "src/api/constants";
|
||||
|
||||
type SpeechServiceFormProps = {
|
||||
credential?: UseApiDataMap<SpeechCredential>;
|
||||
@@ -77,6 +82,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const [sttApiKey, setSttApiKey] = useState("");
|
||||
const [ttsRegion, setTtsRegion] = useState("");
|
||||
const [ttsApiKey, setTtsApiKey] = useState("");
|
||||
const [ttsModelId, setTtsModelId] = useState(DEFAULT_ELEVENLABS_MODEL);
|
||||
const [instanceId, setInstanceId] = useState("");
|
||||
const [initialCheckCustomTts, setInitialCheckCustomTts] = useState(false);
|
||||
const [initialCheckCustomStt, setInitialCheckCustomStt] = useState(false);
|
||||
@@ -197,6 +203,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
...(vendor === VENDOR_COBALT && {
|
||||
cobalt_server_uri: cobaltServerUri || null,
|
||||
}),
|
||||
...(vendor === VENDOR_ELEVENLABS && {
|
||||
model_id: ttsModelId || null,
|
||||
}),
|
||||
};
|
||||
|
||||
if (credential && credential.data) {
|
||||
@@ -231,7 +240,8 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
vendor === VENDOR_MICROSOFT ||
|
||||
vendor === VENDOR_WELLSAID ||
|
||||
vendor === VENDOR_DEEPGRAM ||
|
||||
vendor === VENDOR_SONIOX
|
||||
vendor === VENDOR_SONIOX ||
|
||||
vendor === VENDOR_ELEVENLABS
|
||||
? apiKey
|
||||
: null,
|
||||
}),
|
||||
@@ -388,6 +398,9 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
if (credential.data.cobalt_server_uri) {
|
||||
setCobaltServerUri(credential.data.cobalt_server_uri);
|
||||
}
|
||||
if (credential.data.model_id) {
|
||||
setTtsModelId(credential.data.model_id);
|
||||
}
|
||||
}
|
||||
}, [credential]);
|
||||
|
||||
@@ -490,18 +503,20 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
<div>Use for text-to-speech</div>
|
||||
</label>
|
||||
)}
|
||||
{vendor !== VENDOR_WELLSAID && vendor !== VENDOR_CUSTOM && (
|
||||
<label htmlFor="use_for_stt" className="chk">
|
||||
<input
|
||||
id="use_for_stt"
|
||||
name="use_for_stt"
|
||||
type="checkbox"
|
||||
onChange={(e) => setSttCheck(e.target.checked)}
|
||||
defaultChecked={sttCheck}
|
||||
/>
|
||||
<div>Use for speech-to-text</div>
|
||||
</label>
|
||||
)}
|
||||
{vendor !== VENDOR_WELLSAID &&
|
||||
vendor !== VENDOR_CUSTOM &&
|
||||
vendor !== VENDOR_ELEVENLABS && (
|
||||
<label htmlFor="use_for_stt" className="chk">
|
||||
<input
|
||||
id="use_for_stt"
|
||||
name="use_for_stt"
|
||||
type="checkbox"
|
||||
onChange={(e) => setSttCheck(e.target.checked)}
|
||||
defaultChecked={sttCheck}
|
||||
/>
|
||||
<div>Use for speech-to-text</div>
|
||||
</label>
|
||||
)}
|
||||
{vendor === VENDOR_CUSTOM && (
|
||||
<Fragment>
|
||||
<Checkzone
|
||||
@@ -773,6 +788,7 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
)}
|
||||
{(vendor === VENDOR_WELLSAID ||
|
||||
vendor === VENDOR_DEEPGRAM ||
|
||||
vendor == VENDOR_ELEVENLABS ||
|
||||
vendor === VENDOR_SONIOX) && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_apikey`}>
|
||||
@@ -789,6 +805,20 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{vendor == VENDOR_ELEVENLABS && (
|
||||
<fieldset>
|
||||
<label htmlFor={`${vendor}_apikey`}>Model</label>
|
||||
<Selector
|
||||
id={"audio_format"}
|
||||
name={"audio_format"}
|
||||
value={ttsModelId}
|
||||
options={ELEVENLABS_MODEL_OPTIONS}
|
||||
onChange={(e) => {
|
||||
setTtsModelId(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</fieldset>
|
||||
)}
|
||||
{regions &&
|
||||
regions[vendor as keyof RegionVendors] &&
|
||||
vendor !== VENDOR_IBM &&
|
||||
|
||||
@@ -112,7 +112,7 @@ export const SpeechServices = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--ender">
|
||||
<section className="filters filters--multi">
|
||||
<ScopedAccess user={user} scope={Scope.service_provider}>
|
||||
<AccountFilter
|
||||
account={[accountSid, setAccountSid]}
|
||||
|
||||
@@ -88,7 +88,7 @@ export const Users = () => {
|
||||
</Icon>
|
||||
</Link>
|
||||
</section>
|
||||
<section className="filters filters--mix">
|
||||
<section className="filters filters--multi">
|
||||
<section>
|
||||
<SearchFilter
|
||||
placeholder="Filter users"
|
||||
|
||||
@@ -19,14 +19,45 @@
|
||||
grid-gap: ui-vars.$px02;
|
||||
}
|
||||
}
|
||||
|
||||
> :first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
&--multi {
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
grid-gap: ui-vars.$px02;
|
||||
|
||||
> :first-child {
|
||||
margin-left: auto;
|
||||
@media (max-width: 1400px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
|
||||
> * {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
> * {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
> * {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
> * {
|
||||
justify-self: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
src/vendor/index.tsx
vendored
9
src/vendor/index.tsx
vendored
@@ -8,6 +8,7 @@ import type {
|
||||
} from "./types";
|
||||
|
||||
export const LANG_EN_US = "en-US";
|
||||
export const ELEVENLABS_LANG_EN = "en";
|
||||
export const LANG_COBALT_EN_US = "en_US-8khz";
|
||||
export const LANG_EN_US_STANDARD_C = "en-US-Standard-C";
|
||||
export const VENDOR_GOOGLE = "google";
|
||||
@@ -21,6 +22,7 @@ export const VENDOR_NVIDIA = "nvidia";
|
||||
export const VENDOR_SONIOX = "soniox";
|
||||
export const VENDOR_CUSTOM = "custom";
|
||||
export const VENDOR_COBALT = "cobalt";
|
||||
export const VENDOR_ELEVENLABS = "elevenlabs";
|
||||
|
||||
export const vendors: VendorOptions[] = [
|
||||
{
|
||||
@@ -67,6 +69,10 @@ export const vendors: VendorOptions[] = [
|
||||
name: "Cobalt",
|
||||
value: VENDOR_COBALT,
|
||||
},
|
||||
{
|
||||
name: "ElevenLabs",
|
||||
value: VENDOR_ELEVENLABS,
|
||||
},
|
||||
].sort((a, b) => a.name.localeCompare(b.name)) as VendorOptions[];
|
||||
|
||||
export const useRegionVendors = () => {
|
||||
@@ -129,6 +135,7 @@ export const useSpeechVendors = () => {
|
||||
import("./speech-synthesis/nuance-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/ibm-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/nvidia-speech-synthesis-lang"),
|
||||
import("./speech-synthesis/elevellabs-speech-synthesis-lang"),
|
||||
]).then(
|
||||
([
|
||||
{ default: awsRecognizer },
|
||||
@@ -147,6 +154,7 @@ export const useSpeechVendors = () => {
|
||||
{ default: nuanceSynthesis },
|
||||
{ default: ibmSynthesis },
|
||||
{ default: nvidiaynthesis },
|
||||
{ default: elevenLabsSynthesis },
|
||||
]) => {
|
||||
if (!ignore) {
|
||||
setSpeech({
|
||||
@@ -158,6 +166,7 @@ export const useSpeechVendors = () => {
|
||||
nuance: nuanceSynthesis,
|
||||
ibm: ibmSynthesis,
|
||||
nvidia: nvidiaynthesis,
|
||||
elevenlabs: elevenLabsSynthesis,
|
||||
},
|
||||
recognizers: {
|
||||
aws: awsRecognizer,
|
||||
|
||||
196
src/vendor/speech-synthesis/elevellabs-speech-synthesis-lang.ts
vendored
Normal file
196
src/vendor/speech-synthesis/elevellabs-speech-synthesis-lang.ts
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
import type { VoiceLanguage } from "../types";
|
||||
|
||||
export const languages: VoiceLanguage[] = [
|
||||
{
|
||||
code: "ar",
|
||||
name: "Arabic",
|
||||
voices: [
|
||||
{
|
||||
value: "pNInz6obpgDQGcFmaJgB",
|
||||
name: "Adam - american, deep, middle aged, male, narration",
|
||||
},
|
||||
{
|
||||
value: "ErXwobaYiN019PkySvjV",
|
||||
name: "Antoni - american, well-rounded, young, male, narration",
|
||||
},
|
||||
{
|
||||
value: "VR6AewLTigWG4xSOukaG",
|
||||
name: "Arnold - american, crisp, middle aged, male, narration",
|
||||
},
|
||||
{
|
||||
value: "EXAVITQu4vr4xnSDxMaL",
|
||||
name: "Bella - american, soft, young, female, narration",
|
||||
},
|
||||
{
|
||||
value: "N2lVS1w4EtoT3dr4eOWO",
|
||||
name: "Callum - american, hoarse, middle aged, male, video games",
|
||||
},
|
||||
{
|
||||
value: "IKne3meq5aSn9XLyUdCD",
|
||||
name: "Charlie - australian, casual, middle aged, male, conversational",
|
||||
},
|
||||
{
|
||||
value: "XB0fDUnXU5powFXDhCwa",
|
||||
name: "Charlotte - english-swedish, seductive, middle aged, female, video games",
|
||||
},
|
||||
{
|
||||
value: "2EiwWnXFnvU5JabPnv8n",
|
||||
name: "Clyde - american, war veteran, middle aged, male, video games",
|
||||
},
|
||||
{
|
||||
value: "onwK4e9ZLuTAKqWW03F9",
|
||||
name: "Daniel - british, deep, middle aged, male, news presenter",
|
||||
},
|
||||
{
|
||||
value: "CYw3kZ02Hs0563khs1Fj",
|
||||
name: "Dave - british-essex, conversational, young, male, video games",
|
||||
},
|
||||
{
|
||||
value: "AZnzlk1XvdvUeBnXmlld",
|
||||
name: "Domi - american, strong, young, female, narration",
|
||||
},
|
||||
{
|
||||
value: "ThT5KcBeYPX3keUQqHPh",
|
||||
name: "Dorothy - british, pleasant, young, female, children's stories",
|
||||
},
|
||||
{
|
||||
value: "MF3mGyEYCl7XYWbV9V6O",
|
||||
name: "Elli - american, emotional, young, female, narration",
|
||||
},
|
||||
{
|
||||
value: "LcfcDJNUP1GQjkzn1xUU",
|
||||
name: "Emily - american, calm, young, female, meditation",
|
||||
},
|
||||
{
|
||||
value: "g5CIjZEefAph4nQFvHAz",
|
||||
name: "Ethan - american, undefined, young, male, ASMR",
|
||||
},
|
||||
{
|
||||
value: "D38z5RcWu1voky8WS1ja",
|
||||
name: "Fin - irish, sailor, old, male, video games",
|
||||
},
|
||||
{
|
||||
value: "jsCqWAovK2LkecY7zXl4",
|
||||
name: "Freya - american, undefined, young, female, undefined",
|
||||
},
|
||||
{
|
||||
value: "jBpfuIE2acCO8z3wKNLl",
|
||||
name: "Gigi - american, childlish, young, female, animation",
|
||||
},
|
||||
{
|
||||
value: "zcAOhNBS3c14rBihAFp1",
|
||||
name: "Giovanni - english-italian, foreigner, young, male, audiobook",
|
||||
},
|
||||
{
|
||||
value: "z9fAnlkpzviPz146aGWa",
|
||||
name: "Glinda - american, witch, middle aged, female, video games",
|
||||
},
|
||||
{
|
||||
value: "oWAxZDx7w5VEj9dCyTzz",
|
||||
name: "Grace - american-southern, undefined, young, female, audiobook ",
|
||||
},
|
||||
{
|
||||
value: "SOYHLrjzK2X1ezoPC6cr",
|
||||
name: "Harry - american, anxious, young, male, video games",
|
||||
},
|
||||
{
|
||||
value: "ZQe5CZNOzWyzPSCn5a3c",
|
||||
name: "James - australian, calm , old, male, news",
|
||||
},
|
||||
{
|
||||
value: "bVMeCyTHy58xNoL34h3p",
|
||||
name: "Jeremy - american-irish, excited, young, male, narration",
|
||||
},
|
||||
{
|
||||
value: "t0jbNlBVZ17f02VDIeMI",
|
||||
name: "Jessie - american, raspy , old, male, video games",
|
||||
},
|
||||
{
|
||||
value: "Zlb1dXrM653N07WRdFW3",
|
||||
name: "Joseph - british, undefined, middle aged, male, news",
|
||||
},
|
||||
{
|
||||
value: "TxGEqnHWrfWFTfGW9XjX",
|
||||
name: "Josh - american, deep, young, male, narration",
|
||||
},
|
||||
{
|
||||
value: "TX3LPaxmHKxFdv7VOQHJ",
|
||||
name: "Liam - american, undefined, young, male, narration",
|
||||
},
|
||||
{
|
||||
value: "XrExE9yKIg1WjnnlVkGX",
|
||||
name: "Matilda - american, warm, young, female, audiobook",
|
||||
},
|
||||
{
|
||||
value: "Yko7PKHZNXotIFUBG7I9",
|
||||
name: "Matthew - british, undefined, middle aged, male, audiobook",
|
||||
},
|
||||
{
|
||||
value: "flq6f7yk4E4fJM5XTYuZ",
|
||||
name: "Michael - american, undefined, old, male, audiobook",
|
||||
},
|
||||
{
|
||||
value: "zrHiDhphv9ZnVXBqCLjz",
|
||||
name: "Mimi - english-swedish, childish, young, female, animation",
|
||||
},
|
||||
{
|
||||
value: "piTKgcLEGmPE4e6mEKli",
|
||||
name: "Nicole - american, whisper, young, female, audiobook",
|
||||
},
|
||||
{
|
||||
value: "ODq5zmih8GrVes37Dizd",
|
||||
name: "Patrick - american, shouty, middle aged, male, video games",
|
||||
},
|
||||
{
|
||||
value: "21m00Tcm4TlvDq8ikWAM",
|
||||
name: "Rachel - american, calm, young, female, narration",
|
||||
},
|
||||
{
|
||||
value: "wViXBPUzp2ZZixB1xQuM",
|
||||
name: "Ryan - american, soldier, middle aged, male, audiobook",
|
||||
},
|
||||
{
|
||||
value: "yoZ06aMxZJJ28mfd3POQ",
|
||||
name: "Sam - american, raspy, young, male, narration",
|
||||
},
|
||||
{
|
||||
value: "pMsXgVXv3BLzUgSXRplE",
|
||||
name: "Serena - american, pleasant, middle aged, female, interactive",
|
||||
},
|
||||
{
|
||||
value: "GBv7mTt0atIp3Br8iCZE",
|
||||
name: "Thomas - american, calm, young, male, meditation",
|
||||
},
|
||||
],
|
||||
},
|
||||
{ code: "bg", name: "Bulgarian", voices: [] },
|
||||
{ code: "zh", name: "Chinese", voices: [] },
|
||||
{ code: "hr", name: "Croatian", voices: [] },
|
||||
{ code: "cs", name: "Czech", voices: [] },
|
||||
{ code: "da", name: "Danish", voices: [] },
|
||||
{ code: "nl", name: "Dutch", voices: [] },
|
||||
{ code: "en", name: "English", voices: [] },
|
||||
{ code: "fil", name: "Filipino", voices: [] },
|
||||
{ code: "fi", name: "Finnish", voices: [] },
|
||||
{ code: "fr", name: "French", voices: [] },
|
||||
{ code: "de", name: "German", voices: [] },
|
||||
{ code: "el", name: "Greek", voices: [] },
|
||||
{ code: "hi", name: "Hindi", voices: [] },
|
||||
{ code: "id", name: "Indonesian", voices: [] },
|
||||
{ code: "it", name: "Italian", voices: [] },
|
||||
{ code: "ja", name: "Japanese", voices: [] },
|
||||
{ code: "ko", name: "Korean", voices: [] },
|
||||
{ code: "ms", name: "Malay", voices: [] },
|
||||
{ code: "pl", name: "Polish", voices: [] },
|
||||
{ code: "pt", name: "Portuguese", voices: [] },
|
||||
{ code: "ro", name: "Romanian", voices: [] },
|
||||
{ code: "ru", name: "Russian", voices: [] },
|
||||
{ code: "sk", name: "Slovak", voices: [] },
|
||||
{ code: "es", name: "Spanish", voices: [] },
|
||||
{ code: "sv", name: "Swedish", voices: [] },
|
||||
{ code: "ta", name: "Tamil", voices: [] },
|
||||
{ code: "tr", name: "Turkish", voices: [] },
|
||||
{ code: "uk", name: "Ukrainian", voices: [] },
|
||||
];
|
||||
|
||||
export default languages;
|
||||
4
src/vendor/types.ts
vendored
4
src/vendor/types.ts
vendored
@@ -9,7 +9,8 @@ export type Vendor =
|
||||
| "Nvidia"
|
||||
| "Soniox"
|
||||
| "Cobalt"
|
||||
| "Custom";
|
||||
| "Custom"
|
||||
| "ElevenLabs";
|
||||
|
||||
export interface VendorOptions {
|
||||
name: Vendor;
|
||||
@@ -82,6 +83,7 @@ export interface SynthesisVendors {
|
||||
nuance: VoiceLanguage[];
|
||||
ibm: VoiceLanguage[];
|
||||
nvidia: VoiceLanguage[];
|
||||
elevenlabs: VoiceLanguage[];
|
||||
}
|
||||
|
||||
export interface MSRawSpeech {
|
||||
|
||||
Reference in New Issue
Block a user