From a7c00953a3b3152a47d746c9805bbbe64cf94c86 Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 15 Nov 2023 00:13:09 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20Refactor?= =?UTF-8?q?=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +-- api/azure-speech.ts | 28 ---- api/edge-speech.ts | 13 ++ api/microsoft-speech.ts | 12 +- api/open-stt.ts | 19 +++ api/openai-tts.ts | 19 +++ package.json | 3 +- src/const/api.ts | 32 ++-- src/data/{nameList.ts => voiceList.ts} | 0 src/index.ts | 10 +- src/server.ts | 7 +- src/server/cors.ts | 140 ------------------ src/server/createEdgeSpeechComletion.ts | 103 +++++++++++++ ...t.ts => createMicrosoftSpeechComletion.ts} | 28 +++- .../createOpenaiAudioSpeechCompletion.ts | 26 ++++ ...eateOpenaiAudioTranscriptionsCompletion.ts | 29 ++++ src/server/getAllowOrigins.ts | 15 -- src/server/handleAzureSpeechRequest.ts | 52 ------- src/server/types.ts | 58 ++++++++ src/services/fetchAzureSpeech.ts | 39 ----- src/services/fetchEdgeSpeech.ts | 109 ++------------ src/services/fetchMicrosoftSpeech.ts | 32 ++-- src/services/fetchOpenaiSTT.ts | 45 +++--- src/services/fetchOpenaiTTS.ts | 41 ++--- src/useAzureSpeech/demos/index.tsx | 84 ----------- src/useAzureSpeech/index.md | 11 -- src/useAzureSpeech/index.ts | 15 -- src/useEdgeSpeech/demos/index.tsx | 19 ++- src/useEdgeSpeech/index.ts | 2 +- src/useMicrosoftSpeech/demos/index.tsx | 34 +++-- src/useMicrosoftSpeech/index.md | 2 +- src/useMicrosoftSpeech/index.ts | 2 +- src/useOpenaiSTT/demos/index.tsx | 3 +- src/useOpenaiTTS/demos/index.tsx | 5 +- src/useOpenaiTTS/index.ts | 2 +- src/useSpeechSynthes/demos/index.tsx | 8 +- src/useSpeechSynthes/index.ts | 6 +- src/utils/genSSML.ts | 34 +++-- src/utils/getVoiceList.ts | 6 +- 39 files changed, 477 insertions(+), 638 deletions(-) delete mode 100644 api/azure-speech.ts create mode 100644 api/edge-speech.ts create mode 100644 api/open-stt.ts create mode 100644 api/openai-tts.ts rename src/data/{nameList.ts => voiceList.ts} (100%) delete mode 100644 src/server/cors.ts create mode 100644 src/server/createEdgeSpeechComletion.ts rename src/server/{handleMicrosoftSpeechRequest.ts => createMicrosoftSpeechComletion.ts} (55%) create mode 100644 src/server/createOpenaiAudioSpeechCompletion.ts create mode 100644 src/server/createOpenaiAudioTranscriptionsCompletion.ts delete mode 100644 src/server/getAllowOrigins.ts delete mode 100644 src/server/handleAzureSpeechRequest.ts create mode 100644 src/server/types.ts delete mode 100644 src/services/fetchAzureSpeech.ts delete mode 100644 src/useAzureSpeech/demos/index.tsx delete mode 100644 src/useAzureSpeech/index.md delete mode 100644 src/useAzureSpeech/index.ts diff --git a/README.md b/README.md index f1c9683..f01621d 100644 --- a/README.md +++ b/README.md @@ -86,17 +86,17 @@ Click button below to deploy your private plugins' gateway. This project provides some additional configuration items set with environment variables: -| Environment Variable | Description | Default | -| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | -| `ALLOW_ORIGINS` | Allow origins , string or string array | | -| `OPENAI_API_KEY` | This is the API key you apply on the OpenAI account page | `sk-xxxxxx...xxxxxx` | -| `OPENAI_PROXY_URL` | If you manually configure the OpenAI interface proxy, you can use this configuration item to override the default OpenAI API request base URL | `https://api.openai.com/v1` | -| `AZURE_SPEECH_KEY` | This is the API key of Azure Speech Service | | -| `AZURE_SPEECH_REGION` | This is the region of Azure Speech Service | | -| `AZURE_SPEECH_PROXY_URL` | If you manually configure the AZURE Speech interface proxy, you can use this configuration item to override the default Speech API request base URL | `/api/azure-speech` | -| `MICROSOFT_SPEECH_PROXY_URL` | If you manually configure the Microsoft Speech interface proxy, you can use this configuration item to override the default Speech API request base URL | `/api/microsoft-speech` | -| `EDDGE_API_TOKEN` | This is the API key of Edge Speech Service | | -| `EDDGE_PROXY_URL` | If you manually configure the Edge interface proxy, you can use this configuration item to override the default Edge wss request base URL | | +| Environment Variable | Description | Default | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | +| `ALLOW_ORIGINS` | Allow origins , string or string array | | +| `OPENAI_API_KEY` | This is the API key you apply on the OpenAI account page | `sk-xxxxxx...xxxxxx` | +| `OPENAI_PROXY_URL` | If you manually configure the OpenAI interface proxy, you can use this configuration item to override the default OpenAI API request base URL | `https://api.openai.com/v1` | +| `AZURE_SPEECH_KEY` | This is the API key of Azure Speech Service | | +| `AZURE_SPEECH_REGION` | This is the region of Azure Speech Service | | +| `AZURE_SPEECH_PROXY_URL` | If you manually configure the AZURE Speech interface proxy, you can use this configuration item to override the default Speech API request base URL | `/api/azure-speech` | +| `MICROSOFT_SPEECH_API_URL` | If you manually configure the Microsoft Speech interface proxy, you can use this configuration item to override the default Speech API request base URL | `/api/microsoft-speech` | +| `EDGE_API_TOKEN` | This is the API key of Edge Speech Service | | +| `EDGE_SPEECH_API_URL` | If you manually configure the Edge interface proxy, you can use this configuration item to override the default Edge wss request base URL | |
diff --git a/api/azure-speech.ts b/api/azure-speech.ts deleted file mode 100644 index f6d90da..0000000 --- a/api/azure-speech.ts +++ /dev/null @@ -1,28 +0,0 @@ -// TODO: fix vercel error -// Error: The Edge Function "api/azure-speech" is referencing unsupported modules: -// - https-proxy-agent: net, tls, url -// - microsoft-cognitiveservices-speech-sdk: vc-blob-asset:speech-processor.js, fs, net, tls - -/* -import cors from '../src/server/cors'; -import { getAllowOrigins } from '../src/server/getAllowOrigins'; -import { handleAzureSpeechRequest } from '../src/server/handleAzureSpeechRequest'; - -export const config = { - runtime: 'edge', -}; - -export default async (req: Request) => { - if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); - const origin = getAllowOrigins(req); - if (!origin) return new Response('Origin Not Allowed', { status: 403 }); - const res = await handleAzureSpeechRequest(req); - return cors(req, res, { methods: ['POST'], origin }); -}; -*/ - -export default async (req: Request) => { - if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); - - return new Response('WIP'); -}; diff --git a/api/edge-speech.ts b/api/edge-speech.ts new file mode 100644 index 0000000..c4f9639 --- /dev/null +++ b/api/edge-speech.ts @@ -0,0 +1,13 @@ +import { createEdgeSpeechComletion } from '../src/server/createEdgeSpeechComletion'; +import { EdgeSpeechPayload } from '../src/server/types'; + +export const config = { + runtime: 'edge', +}; + +export default async (req: Request) => { + if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); + const payload = (await req.json()) as EdgeSpeechPayload; + const res = await createEdgeSpeechComletion({ payload }); + return res; +}; diff --git a/api/microsoft-speech.ts b/api/microsoft-speech.ts index 9b62c17..38e3f02 100644 --- a/api/microsoft-speech.ts +++ b/api/microsoft-speech.ts @@ -1,6 +1,5 @@ -import cors from '../src/server/cors'; -import { getAllowOrigins } from '../src/server/getAllowOrigins'; -import { handleMicrosoftSpeechRequest } from '../src/server/handleMicrosoftSpeechRequest'; +import { createMicrosoftSpeechComletion } from '../src/server/createMicrosoftSpeechComletion'; +import { MicrosoftSpeechPayload } from '../src/server/types'; export const config = { runtime: 'edge', @@ -8,8 +7,7 @@ export const config = { export default async (req: Request) => { if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); - const origin = getAllowOrigins(req); - if (!origin) return new Response('Origin Not Allowed', { status: 403 }); - const res = await handleMicrosoftSpeechRequest(req); - return cors(req, new Response(res.body, res), { methods: ['POST'], origin }); + const payload = (await req.json()) as MicrosoftSpeechPayload; + const res = await createMicrosoftSpeechComletion({ payload }); + return res; }; diff --git a/api/open-stt.ts b/api/open-stt.ts new file mode 100644 index 0000000..32ae42b --- /dev/null +++ b/api/open-stt.ts @@ -0,0 +1,19 @@ +import OpenAI from 'openai'; + +import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; + +import { createOpenaiAudioTranscriptionsCompletion } from '../src/server/createOpenaiAudioTranscriptionsCompletion'; +import { OpenAISTTPayload } from '../src/server/types'; + +export const config = { + runtime: 'edge', +}; + +export default async (req: Request) => { + if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); + const payload = (await req.json()) as OpenAISTTPayload; + if (!OPENAI_API_KEY) return new Response('OPENAI_API_KEY is not set', { status: 500 }); + const openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_PROXY_URL }); + const res = await createOpenaiAudioTranscriptionsCompletion({ openai, payload }); + return res; +}; diff --git a/api/openai-tts.ts b/api/openai-tts.ts new file mode 100644 index 0000000..88ecbfe --- /dev/null +++ b/api/openai-tts.ts @@ -0,0 +1,19 @@ +import OpenAI from 'openai'; + +import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; + +import { createOpenaiAudioSpeechCompletion } from '../src/server/createOpenaiAudioSpeechCompletion'; +import { OpenAITTSPayload } from '../src/server/types'; + +export const config = { + runtime: 'edge', +}; + +export default async (req: Request) => { + if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); + const payload = (await req.json()) as OpenAITTSPayload; + if (!OPENAI_API_KEY) return new Response('OPENAI_API_KEY is not set', { status: 500 }); + const openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_PROXY_URL }); + const res = await createOpenaiAudioSpeechCompletion({ openai, payload }); + return res; +}; diff --git a/package.json b/package.json index c8b5aa4..ca9845d 100644 --- a/package.json +++ b/package.json @@ -66,11 +66,10 @@ "antd-style": "^3", "lodash-es": "^4", "lucide-react": "latest", - "microsoft-cognitiveservices-speech-sdk": "^1", + "openai": "^4.17.3", "query-string": "^8", "react-error-boundary": "^4.0.11", "react-layout-kit": "^1", - "ssml-document": "^1", "swr": "^2", "url-join": "^5", "uuid": "^9" diff --git a/src/const/api.ts b/src/const/api.ts index 89afd3c..ca4fbad 100644 --- a/src/const/api.ts +++ b/src/const/api.ts @@ -1,15 +1,14 @@ -import urlJoin from 'url-join'; - -export const MICROSOFT_SPPECH_URL = +export const MICROSOFT_SPEECH_URL = 'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak'; -export const MICROSOFT_SPEECH_PROXY_URL = - process.env.MICROSOFT_SPEECH_PROXY_URL || - process.env.NEXT_PUBLIC_MICROSOFT_SPEECH_PROXY_URL || - '/api/microsoft-speech'; -export const AZURE_SPEECH_PROXY_URL = - process.env.AZURE_SPEECH_PROXY_URL || - process.env.NEXT_PUBLIC_AZURE_SPEECH_PROXY_URL || - '/api/azure-speech'; +export const EDGE_SPEECH_URL = + 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1'; +export const EDGE_API_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; + +export const MICROSOFT_SPEECH_API_URL = '/api/microsoft-speech'; +export const EDGE_SPEECH_API_URL = '/api/edge-speech'; +export const OPENAI_TTS_API_URL = '/api/openai-tts'; +export const OPENAI_STT_API_URL = '/api/openai-stt'; + export const AZURE_SPEECH_KEY = process.env.AZURE_SPEECH_KEY || process.env.NEXT_PUBLIC_AZURE_SPEECH_KEY || ''; export const AZURE_SPEECH_REGION = @@ -20,14 +19,3 @@ export const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL || process.env.NEXT_PUBLIC_OPENAI_PROXY_URL || 'https://api.openai.com/v1'; -export const OPENAI_TTS_URL = (api?: string) => urlJoin(api || OPENAI_PROXY_URL, 'audio/speech'); -export const OPENAI_STT_URL = (api?: string) => - urlJoin(api || OPENAI_PROXY_URL, 'audio/transcriptions'); -export const EDDGE_PROXY_URL = - process.env.EDDGE_PROXY_URL || - process.env.NEXT_PUBLIC_EDDGE_PROXY_UR || - 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1'; -export const EDDGE_API_TOKEN = - process.env.EDDGE_API_TOKEN || - process.env.NEXT_PUBLIC_EDDGE_API_TOKEN || - '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; diff --git a/src/data/nameList.ts b/src/data/voiceList.ts similarity index 100% rename from src/data/nameList.ts rename to src/data/voiceList.ts diff --git a/src/index.ts b/src/index.ts index 5f2b637..985450d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,19 +3,17 @@ export { default as AudioVisualizer, type AudioVisualizerProps } from './AudioVi export { default as azureVoiceList } from './data/azureVoiceList'; export { default as edgeVoiceList } from './data/edgeVoiceList'; export { default as voiceLocale } from './data/locales'; -export { default as nameList } from './data/nameList'; export { default as openaiVoiceList } from './data/openaiVoiceList'; +export { default as voiceList } from './data/voiceList'; export { useAudioPlayer } from './hooks/useAudioPlayer'; export { useAudioVisualizer } from './hooks/useAudioVisualizer'; export { useBlobUrl } from './hooks/useBlobUrl'; export { useStreamAudioPlayer } from './hooks/useStreamAudioPlayer'; -export { type AzureSpeechOptions, fetchAzureSpeech } from './services/fetchAzureSpeech'; export { type EdgeSpeechOptions, fetchEdgeSpeech } from './services/fetchEdgeSpeech'; export { fetchMicrosoftSpeech, type MicrosoftSpeechOptions } from './services/fetchMicrosoftSpeech'; export { fetchOpenaiSTT, type OpenaiSttOptions } from './services/fetchOpenaiSTT'; export { fetchOpenaiTTS, type OpenaiTtsOptions } from './services/fetchOpenaiTTS'; export { useAudioRecorder } from './useAudioRecorder'; -export { useAzureSpeech } from './useAzureSpeech'; export { useEdgeSpeech } from './useEdgeSpeech'; export { useMicrosoftSpeech } from './useMicrosoftSpeech'; export { @@ -42,3 +40,9 @@ export { getSpeechSynthesVoiceOptions, getVoiceLocaleOptions, } from './utils/getVoiceList'; +export { + EDGE_SPEECH_API_URL, + MICROSOFT_SPEECH_API_URL, + OPENAI_STT_API_URL, + OPENAI_TTS_API_URL, +} from '@/const/api'; diff --git a/src/server.ts b/src/server.ts index 4c232a9..114b555 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,2 +1,5 @@ -export { handleAzureSpeechRequest } from './server/handleAzureSpeechRequest'; -export { handleMicrosoftSpeechRequest } from './server/handleMicrosoftSpeechRequest'; +export { createEdgeSpeechComletion } from '@/server/createEdgeSpeechComletion'; +export { createMicrosoftSpeechComletion } from '@/server/createMicrosoftSpeechComletion'; +export { createOpenaiAudioSpeechCompletion } from '@/server/createOpenaiAudioSpeechCompletion'; +export { createOpenaiAudioTranscriptionsCompletion } from '@/server/createOpenaiAudioTranscriptionsCompletion'; +export * from '@/server/types'; diff --git a/src/server/cors.ts b/src/server/cors.ts deleted file mode 100644 index a8be6c5..0000000 --- a/src/server/cors.ts +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Multi purpose CORS lib. - * Note: Based on the `cors` package in npm but using only - * web APIs. Feel free to use it in your own projects. - */ - -type StaticOrigin = boolean | string | RegExp | (boolean | string | RegExp)[]; - -type OriginFn = (origin: string | undefined, req: Request) => StaticOrigin | Promise; - -interface CorsOptions { - allowedHeaders?: string | string[]; - credentials?: boolean; - exposedHeaders?: string | string[]; - maxAge?: number; - methods?: string | string[]; - optionsSuccessStatus?: number; - origin?: StaticOrigin | OriginFn; - preflightContinue?: boolean; -} - -const defaultOptions: CorsOptions = { - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', - optionsSuccessStatus: 204, - origin: '*', - preflightContinue: false, -}; - -function isOriginAllowed(origin: string, allowed: StaticOrigin): boolean { - return Array.isArray(allowed) - ? allowed.some((o) => isOriginAllowed(origin, o)) - : typeof allowed === 'string' - ? origin === allowed - : allowed instanceof RegExp - ? allowed.test(origin) - : !!allowed; -} - -function getOriginHeaders(reqOrigin: string | undefined, origin: StaticOrigin) { - const headers = new Headers(); - - if (origin === '*') { - // Allow any origin - headers.set('Access-Control-Allow-Origin', '*'); - } else if (typeof origin === 'string') { - // Fixed origin - headers.set('Access-Control-Allow-Origin', origin); - headers.append('Vary', 'Origin'); - } else { - const allowed = isOriginAllowed(reqOrigin ?? '', origin); - - if (allowed && reqOrigin) { - headers.set('Access-Control-Allow-Origin', reqOrigin); - } - headers.append('Vary', 'Origin'); - } - - return headers; -} - -// originHeadersFromReq - -async function originHeadersFromReq(req: Request, origin: StaticOrigin | OriginFn) { - const reqOrigin = req.headers.get('Origin') || undefined; - const value = typeof origin === 'function' ? await origin(reqOrigin, req) : origin; - - if (!value) return; - return getOriginHeaders(reqOrigin, value); -} - -function getAllowedHeaders(req: Request, allowed?: string | string[]) { - const headers = new Headers(); - - if (!allowed) { - allowed = req.headers.get('Access-Control-Request-Headers')!; - headers.append('Vary', 'Access-Control-Request-Headers'); - } else if (Array.isArray(allowed)) { - // If the allowed headers is an array, turn it into a string - allowed = allowed.join(','); - } - if (allowed) { - headers.set('Access-Control-Allow-Headers', allowed); - } - - return headers; -} - -export default async function cors(req: Request, res: Response, options?: CorsOptions) { - const opts = { ...defaultOptions, ...options }; - const { headers } = res; - const originHeaders = await originHeadersFromReq(req, opts.origin ?? false); - const mergeHeaders = (v: string, k: string) => { - if (k === 'Vary') headers.append(k, v); - else headers.set(k, v); - }; - - // If there's no origin we won't touch the response - if (!originHeaders) return res; - - originHeaders.forEach(mergeHeaders); - - if (opts.credentials) { - headers.set('Access-Control-Allow-Credentials', 'true'); - } - - const exposed = Array.isArray(opts.exposedHeaders) - ? opts.exposedHeaders.join(',') - : opts.exposedHeaders; - - if (exposed) { - headers.set('Access-Control-Expose-Headers', exposed); - } - - // Handle the preflight request - if (req.method === 'OPTIONS') { - if (opts.methods) { - const methods = Array.isArray(opts.methods) ? opts.methods.join(',') : opts.methods; - - headers.set('Access-Control-Allow-Methods', methods); - } - - getAllowedHeaders(req, opts.allowedHeaders).forEach(mergeHeaders); - - if (typeof opts.maxAge === 'number') { - headers.set('Access-Control-Max-Age', String(opts.maxAge)); - } - - if (opts.preflightContinue) return res; - - headers.set('Content-Length', '0'); - return new Response(null, { headers, status: opts.optionsSuccessStatus }); - } - - // If we got here, it's a normal request - return res; -} - -export function initCors(options?: CorsOptions) { - return (req: Request, res: Response) => cors(req, res, options); -} diff --git a/src/server/createEdgeSpeechComletion.ts b/src/server/createEdgeSpeechComletion.ts new file mode 100644 index 0000000..c0a478c --- /dev/null +++ b/src/server/createEdgeSpeechComletion.ts @@ -0,0 +1,103 @@ +import qs from 'query-string'; +import { v4 as uuidv4 } from 'uuid'; + +import { EDGE_API_TOKEN, EDGE_SPEECH_URL } from '../const/api'; +import { EdgeSpeechPayload } from '../server/types'; +import { genSSML } from '../utils/genSSML'; +import { genSendContent } from '../utils/genSendContent'; +import { getHeadersAndData } from '../utils/getHeadersAndData'; + +const configConent = JSON.stringify({ + context: { + synthesis: { + audio: { + metadataoptions: { sentenceBoundaryEnabled: false, wordBoundaryEnabled: true }, + outputFormat: 'audio-24khz-48kbitrate-mono-mp3', + }, + }, + }, +}); + +const genHeader = (connectId: string) => { + const date = new Date().toString(); + const configHeader = { + 'Content-Type': 'application/json; charset=utf-8', + 'Path': 'speech.config', + 'X-Timestamp': date, + }; + const contentHeader = { + 'Content-Type': 'application/ssml+xml', + 'Path': 'ssml', + 'X-RequestId': connectId, + 'X-Timestamp': date, + }; + return { + configHeader, + contentHeader, + }; +}; + +interface CreateEdgeSpeechComletionOptions { + payload: EdgeSpeechPayload; +} + +export const createEdgeSpeechComletion = async ({ + payload, +}: CreateEdgeSpeechComletionOptions): Promise => { + const { input, options } = payload; + + const connectId = uuidv4().replaceAll('-', ''); + const url = qs.stringifyUrl({ + query: { + ConnectionId: connectId, + TrustedClientToken: EDGE_API_TOKEN, + }, + url: EDGE_SPEECH_URL, + }); + + const { configHeader, contentHeader } = genHeader(connectId); + const config = genSendContent(configHeader, configConent); + const content = genSendContent(contentHeader, genSSML(input, options)); + + return new Promise((resolve, reject) => { + const ws = new WebSocket(url); + ws.binaryType = 'arraybuffer'; + const onOpen = () => { + ws.send(config); + ws.send(content); + }; + let audioData = new ArrayBuffer(0); + const onMessage = async (event: MessageEvent) => { + if (typeof event.data === 'string') { + const { headers } = getHeadersAndData(event.data); + switch (headers['Path']) { + case 'turn.end': { + ws.close(); + if (!audioData.byteLength) return; + const res = new Response(audioData); + resolve(res); + break; + } + } + } else if (event.data instanceof ArrayBuffer) { + const dataview = new DataView(event.data); + const headerLength = dataview.getInt16(0); + if (event.data.byteLength > headerLength + 2) { + const newBody = event.data.slice(2 + headerLength); + const newAudioData = new ArrayBuffer(audioData.byteLength + newBody.byteLength); + const mergedUint8Array = new Uint8Array(newAudioData); + mergedUint8Array.set(new Uint8Array(audioData), 0); + mergedUint8Array.set(new Uint8Array(newBody), audioData.byteLength); + audioData = newAudioData; + } + } + }; + const onError = () => { + reject(new Error('WebSocket error occurred.')); + ws.close(); + }; + ws.addEventListener('open', onOpen); + ws.addEventListener('message', onMessage); + ws.addEventListener('error', onError); + }); +}; diff --git a/src/server/handleMicrosoftSpeechRequest.ts b/src/server/createMicrosoftSpeechComletion.ts similarity index 55% rename from src/server/handleMicrosoftSpeechRequest.ts rename to src/server/createMicrosoftSpeechComletion.ts index 1032970..9b2f854 100644 --- a/src/server/handleMicrosoftSpeechRequest.ts +++ b/src/server/createMicrosoftSpeechComletion.ts @@ -1,8 +1,18 @@ import { v4 as uuidv4 } from 'uuid'; -import { MICROSOFT_SPPECH_URL } from '../const/api'; +import { MICROSOFT_SPEECH_URL } from '../const/api'; +import { MicrosoftSpeechPayload } from '../server/types'; +import { genSSML } from '../utils/genSSML'; + +interface CreateMicrosoftSpeechComletionOptions { + payload: MicrosoftSpeechPayload; +} + +export const createMicrosoftSpeechComletion = async ({ + payload, +}: CreateMicrosoftSpeechComletionOptions) => { + const { input, options } = payload; -export const handleMicrosoftSpeechRequest = async (req: Request, options?: any) => { const DEFAULT_HEADERS = new Headers({ 'accept': '*/*', 'accept-language': 'zh-CN,zh;q=0.9', @@ -20,13 +30,21 @@ export const handleMicrosoftSpeechRequest = async (req: Request, options?: any) 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36', }); - const res = await fetch(MICROSOFT_SPPECH_URL, { - body: req.body, + const body = JSON.stringify({ + offsetInPlainText: 0, + properties: { + SpeakTriggerSource: 'AccTuningPagePlayButton', + }, + ssml: genSSML(input, options), + ttsAudioFormat: 'audio-24khz-160kbitrate-mono-mp3', + }); + + const res = await fetch(MICROSOFT_SPEECH_URL, { + body, headers: DEFAULT_HEADERS, method: 'POST', // @ts-ignore responseType: 'arraybuffer', - ...options, }); return res; diff --git a/src/server/createOpenaiAudioSpeechCompletion.ts b/src/server/createOpenaiAudioSpeechCompletion.ts new file mode 100644 index 0000000..8b0a1fe --- /dev/null +++ b/src/server/createOpenaiAudioSpeechCompletion.ts @@ -0,0 +1,26 @@ +import OpenAI from 'openai'; + +import { OpenAITTSPayload } from './types'; + +interface CreateOpenaiAudioSpeechCompletionOptions { + openai: OpenAI; + payload: OpenAITTSPayload; +} + +export const createOpenaiAudioSpeechCompletion = async ({ + payload, + openai, +}: CreateOpenaiAudioSpeechCompletionOptions) => { + const { options, input } = payload; + + const response = await openai.audio.speech.create( + { + input, + model: options.model, + voice: options.voice, + }, + { headers: { Accept: '*/*' } }, + ); + + return response; +}; diff --git a/src/server/createOpenaiAudioTranscriptionsCompletion.ts b/src/server/createOpenaiAudioTranscriptionsCompletion.ts new file mode 100644 index 0000000..7c3ec30 --- /dev/null +++ b/src/server/createOpenaiAudioTranscriptionsCompletion.ts @@ -0,0 +1,29 @@ +import OpenAI from 'openai'; + +import { OpenAISTTPayload } from './types'; + +interface CreateOpenaiAudioTranscriptionsOptions { + openai: OpenAI; + payload: OpenAISTTPayload; +} + +export const createOpenaiAudioTranscriptionsCompletion = async ({ + payload, + openai, +}: CreateOpenaiAudioTranscriptionsOptions) => { + const { blob, options } = payload; + + const file = new File([blob], `${Date.now()}.${options.mineType.extension}`, { + type: options.mineType.mineType, + }); + + const response = await openai.audio.transcriptions.create( + { + file, + model: options.model, + }, + { headers: { Accept: '*/*' } }, + ); + + return response.text; +}; diff --git a/src/server/getAllowOrigins.ts b/src/server/getAllowOrigins.ts deleted file mode 100644 index 3fccfdf..0000000 --- a/src/server/getAllowOrigins.ts +++ /dev/null @@ -1,15 +0,0 @@ -const ALLOW_ORIGINS = process.env?.ALLOW_ORIGINS?.split(',') || undefined; - -export const getAllowOrigins = (req: Request) => { - let origin = '*'; - - if (ALLOW_ORIGINS) { - const reqOrigin = req.headers.get('origin'); - if (reqOrigin && ALLOW_ORIGINS.includes(reqOrigin)) { - origin = reqOrigin; - } else { - return; - } - } - return origin; -}; diff --git a/src/server/handleAzureSpeechRequest.ts b/src/server/handleAzureSpeechRequest.ts deleted file mode 100644 index 9c9033f..0000000 --- a/src/server/handleAzureSpeechRequest.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - AudioConfig, - PropertyId, - ResultReason, - SpeechConfig, - SpeechSynthesisOutputFormat, - SpeechSynthesisResult, - SpeechSynthesizer, -} from 'microsoft-cognitiveservices-speech-sdk'; - -import { AZURE_SPEECH_KEY, AZURE_SPEECH_REGION } from '../const/api'; - -const fetchAzureSpeech = async (ssml: string, { api }: any): Promise => { - const key = api.key || AZURE_SPEECH_KEY; - const region = api.key || AZURE_SPEECH_REGION; - const speechConfig = SpeechConfig.fromSubscription(key, region); - speechConfig.setProperty(PropertyId.SpeechServiceResponse_RequestSentenceBoundary, 'true'); - speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Webm24Khz16BitMonoOpus; - - const audioConfig = AudioConfig.fromDefaultSpeakerOutput(); - const synthesizer: SpeechSynthesizer | null = new SpeechSynthesizer(speechConfig, audioConfig); - - const completeCb = async ( - result: SpeechSynthesisResult, - resolve: (value: ArrayBuffer) => void, - ) => { - if (result.reason === ResultReason.SynthesizingAudioCompleted) { - const audioData = result.audioData; - resolve(audioData); - } - synthesizer.close(); - }; - - const errCb = (err: string, reject: (err?: any) => void) => { - reject(err); - synthesizer.close(); - }; - - return new Promise((resolve, reject) => { - synthesizer.speakSsmlAsync( - ssml, - (result) => completeCb(result, resolve), - (err) => errCb(err, reject), - ); - }); -}; - -export const handleAzureSpeechRequest = async (req: Request) => { - const { ssml, ...options } = req.body as any; - const data = await fetchAzureSpeech(ssml, options); - return new Response(data); -}; diff --git a/src/server/types.ts b/src/server/types.ts new file mode 100644 index 0000000..4ddfbdf --- /dev/null +++ b/src/server/types.ts @@ -0,0 +1,58 @@ +import { SsmlOptions } from '@/utils/genSSML'; +import { RecordMineType } from '@/utils/getRecordMineType'; + +export interface MicrosoftSpeechPayload { + /** + * @title 语音合成的文本 + */ + input: string; + /** + * @title SSML 语音合成的配置 + */ + options: SsmlOptions; +} + +export interface EdgeSpeechPayload { + /** + * @title 语音合成的文本 + */ + input: string; + /** + * @title SSML 语音合成的配置 + */ + options: Pick; +} + +export interface OpenAITTSPayload { + /** + * @title 语音合成的文本 + */ + input: string; + options: { + /** + * @title 语音合成的模型名称 + */ + model: string; + /** + * @title 语音合成的声音名称 + */ + voice: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer'; + }; +} + +export interface OpenAISTTPayload { + /** + * @title 语音识别的文件 + */ + blob: Blob; + options: { + /** + * @title 语音文件格式 + */ + mineType: RecordMineType; + /** + * @title 语音识别的模型名称 + */ + model: string; + }; +} diff --git a/src/services/fetchAzureSpeech.ts b/src/services/fetchAzureSpeech.ts deleted file mode 100644 index ea495a3..0000000 --- a/src/services/fetchAzureSpeech.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { AZURE_SPEECH_KEY, AZURE_SPEECH_PROXY_URL, AZURE_SPEECH_REGION } from '@/const/api'; -import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; -import { type SsmlOptions, genSSML } from '@/utils/genSSML'; - -export interface AzureSpeechOptions extends SsmlOptions { - api?: { - key?: string; - proxy?: string; - region?: string; - }; -} - -export const fetchAzureSpeech = async ( - text: string, - { api = {}, ...options }: AzureSpeechOptions, -): Promise => { - const data = JSON.stringify({ - api: { - key: api?.key || AZURE_SPEECH_KEY, - region: api?.region || AZURE_SPEECH_REGION, - }, - ssml: genSSML(text, options), - }); - const url = api?.proxy || AZURE_SPEECH_PROXY_URL; - - const response: Response = await fetch(url, { - body: data, - method: 'POST', - // @ts-ignore - responseType: 'arraybuffer', - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const arrayBuffer = await response.arrayBuffer(); - return await arrayBufferConvert(arrayBuffer); -}; diff --git a/src/services/fetchEdgeSpeech.ts b/src/services/fetchEdgeSpeech.ts index 7ab2b50..9c18349 100644 --- a/src/services/fetchEdgeSpeech.ts +++ b/src/services/fetchEdgeSpeech.ts @@ -1,104 +1,25 @@ -import qs from 'query-string'; -import { v4 as uuidv4 } from 'uuid'; - -import { EDDGE_API_TOKEN, EDDGE_PROXY_URL } from '@/const/api'; +import { createEdgeSpeechComletion } from '@/server/createEdgeSpeechComletion'; +import { EdgeSpeechPayload } from '@/server/types'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; -import { type SsmlOptions, genSSML } from '@/utils/genSSML'; -import { genSendContent } from '@/utils/genSendContent'; -import { getHeadersAndData } from '@/utils/getHeadersAndData'; - -const configConent = JSON.stringify({ - context: { - synthesis: { - audio: { - metadataoptions: { sentenceBoundaryEnabled: false, wordBoundaryEnabled: true }, - outputFormat: 'audio-24khz-48kbitrate-mono-mp3', - }, - }, - }, -}); - -const genHeader = (connectId: string) => { - const date = new Date().toString(); - const configHeader = { - 'Content-Type': 'application/json; charset=utf-8', - 'Path': 'speech.config', - 'X-Timestamp': date, - }; - const contentHeader = { - 'Content-Type': 'application/ssml+xml', - 'Path': 'ssml', - 'X-RequestId': connectId, - 'X-Timestamp': date, - }; - return { - configHeader, - contentHeader, - }; -}; +import { type SsmlOptions } from '@/utils/genSSML'; -export interface EdgeSpeechOptions extends Pick { +export interface EdgeSpeechOptions extends Pick { api?: { - key?: string; - proxy?: string; + url?: string; }; } + export const fetchEdgeSpeech = async ( - text: string, - { api = {}, ...options }: EdgeSpeechOptions, + input: string, + { api, ...options }: EdgeSpeechOptions, ): Promise => { - const connectId = uuidv4().replaceAll('-', ''); - const url = qs.stringifyUrl({ - query: { - ConnectionId: connectId, - TrustedClientToken: api?.key || EDDGE_API_TOKEN, - }, - url: api?.proxy || EDDGE_PROXY_URL, - }); + const payload: EdgeSpeechPayload = { input, options }; - const { configHeader, contentHeader } = genHeader(connectId); - const config = genSendContent(configHeader, configConent); - const content = genSendContent(contentHeader, genSSML(text, options)); + const response = await (api?.url + ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) + : await createEdgeSpeechComletion({ payload })); - return new Promise((resolve, reject) => { - const ws = new WebSocket(url); - ws.binaryType = 'arraybuffer'; - const onOpen = () => { - ws.send(config); - ws.send(content); - }; - let audioData = new ArrayBuffer(0); - const onMessage = async (event: MessageEvent) => { - if (typeof event.data === 'string') { - const { headers } = getHeadersAndData(event.data); - switch (headers['Path']) { - case 'turn.end': { - ws.close(); - if (!audioData.byteLength) return; - const audioBuffer = await arrayBufferConvert(audioData); - resolve(audioBuffer); - break; - } - } - } else if (event.data instanceof ArrayBuffer) { - const dataview = new DataView(event.data); - const headerLength = dataview.getInt16(0); - if (event.data.byteLength > headerLength + 2) { - const newBody = event.data.slice(2 + headerLength); - const newAudioData = new ArrayBuffer(audioData.byteLength + newBody.byteLength); - const mergedUint8Array = new Uint8Array(newAudioData); - mergedUint8Array.set(new Uint8Array(audioData), 0); - mergedUint8Array.set(new Uint8Array(newBody), audioData.byteLength); - audioData = newAudioData; - } - } - }; - const onError = () => { - reject(new Error('WebSocket error occurred.')); - ws.close(); - }; - ws.addEventListener('open', onOpen); - ws.addEventListener('message', onMessage); - ws.addEventListener('error', onError); - }); + const arrayBuffer = await response.arrayBuffer(); + const audioBuffer = await arrayBufferConvert(arrayBuffer); + return audioBuffer; }; diff --git a/src/services/fetchMicrosoftSpeech.ts b/src/services/fetchMicrosoftSpeech.ts index 323c80f..4a0594a 100644 --- a/src/services/fetchMicrosoftSpeech.ts +++ b/src/services/fetchMicrosoftSpeech.ts @@ -1,39 +1,29 @@ -import { MICROSOFT_SPEECH_PROXY_URL } from '@/const/api'; +import { createMicrosoftSpeechComletion } from '@/server/createMicrosoftSpeechComletion'; +import { MicrosoftSpeechPayload } from '@/server/types'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; import { type SsmlOptions } from '@/utils/genSSML'; -import { genSSML } from '@/utils/genSSML'; export interface MicrosoftSpeechOptions extends SsmlOptions { api?: { - proxy?: string; + url?: string; }; } export const fetchMicrosoftSpeech = async ( - text: string, - { api = {}, ...options }: MicrosoftSpeechOptions, + input: string, + { api, ...options }: MicrosoftSpeechOptions, ): Promise => { - const data = JSON.stringify({ - offsetInPlainText: 0, - properties: { - SpeakTriggerSource: 'AccTuningPagePlayButton', - }, - ssml: genSSML(text, options), - ttsAudioFormat: 'audio-24khz-160kbitrate-mono-mp3', - }); - const url = api?.proxy || MICROSOFT_SPEECH_PROXY_URL; + const payload: MicrosoftSpeechPayload = { input, options }; - const response: Response = await fetch(url, { - body: data, - method: 'POST', - // @ts-ignore - responseType: 'arraybuffer', - }); + const response = await (api?.url + ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) + : createMicrosoftSpeechComletion({ payload })); if (!response.ok) { throw new Error('Network response was not ok'); } const arrayBuffer = await response.arrayBuffer(); - return await arrayBufferConvert(arrayBuffer); + const audioBuffer = await arrayBufferConvert(arrayBuffer); + return audioBuffer; }; diff --git a/src/services/fetchOpenaiSTT.ts b/src/services/fetchOpenaiSTT.ts index d29e643..4f7a6e4 100644 --- a/src/services/fetchOpenaiSTT.ts +++ b/src/services/fetchOpenaiSTT.ts @@ -1,12 +1,15 @@ -import { v4 as uuidv4 } from 'uuid'; +import OpenAI from 'openai'; -import { OPENAI_API_KEY, OPENAI_STT_URL } from '@/const/api'; +import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; +import { createOpenaiAudioTranscriptionsCompletion } from '@/server/createOpenaiAudioTranscriptionsCompletion'; +import { OpenAISTTPayload } from '@/server/types'; import { RecordMineType, getRecordMineType } from '@/utils/getRecordMineType'; export interface OpenaiSttOptions { api?: { key?: string; proxy?: string; + url?: string; }; mineType?: RecordMineType; model?: 'whisper-1'; @@ -18,28 +21,22 @@ export const fetchOpenaiSTT = async ( { api = {}, model = 'whisper-1', mineType }: OpenaiSttOptions, ): Promise => { const key = api?.key || OPENAI_API_KEY; - const url = OPENAI_STT_URL(api?.proxy); - - const headers = new Headers({ - Authorization: `Bearer ${key}`, - }); - - const filename = `${uuidv4()}.${mineType?.extension || getRecordMineType().extension}`; - const file = new File([speech], filename, { - type: mineType?.mineType || getRecordMineType().mineType, - }); - - const body = new FormData(); - body.append('file', file); - body.append('model', model); - - const response: Response = await fetch(url, { body, headers, method: 'POST' }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } + const url = api?.proxy || OPENAI_PROXY_URL; + + const payload: OpenAISTTPayload = { + blob: speech, + options: { + mineType: mineType || getRecordMineType(), + model, + }, + }; - const json = await response.json(); + const response = (await (api?.url + ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) + : createOpenaiAudioTranscriptionsCompletion({ + openai: new OpenAI({ apiKey: key, baseURL: url }), + payload, + }))) as string; - return json?.text; + return response; }; diff --git a/src/services/fetchOpenaiTTS.ts b/src/services/fetchOpenaiTTS.ts index 75d6218..9007563 100644 --- a/src/services/fetchOpenaiTTS.ts +++ b/src/services/fetchOpenaiTTS.ts @@ -1,36 +1,43 @@ -import { OPENAI_API_KEY, OPENAI_TTS_URL } from '@/const/api'; +import OpenAI from 'openai'; + +import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; +import { createOpenaiAudioSpeechCompletion } from '@/server/createOpenaiAudioSpeechCompletion'; +import { OpenAITTSPayload } from '@/server/types'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; import { type SsmlOptions } from '@/utils/genSSML'; export type OpenaiVoice = 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer'; -export interface OpenaiTtsOptions extends Pick { +export interface OpenaiTtsOptions extends Pick, OpenAITTSPayload { api: { key?: string; proxy?: string; + url?: string; }; model?: 'tts-1' | 'tts-1-hd'; - name: OpenaiVoice | string; + voice: OpenaiVoice; } export const fetchOpenaiTTS = async ( - text: string, - { api = {}, model = 'tts-1', ...options }: OpenaiTtsOptions, + input: string, + { api = {}, model = 'tts-1', voice }: OpenaiTtsOptions, ): Promise => { const key = api?.key || OPENAI_API_KEY; - const url = OPENAI_TTS_URL(api?.proxy); - - const headers = new Headers({ - 'Authorization': `Bearer ${key}`, - 'Content-Type': 'application/json', - }); + const url = api?.proxy || OPENAI_PROXY_URL; - const body = JSON.stringify({ - input: text, - model, - voice: options.name, - }); + const payload: OpenAITTSPayload = { + input, + options: { + model, + voice, + }, + }; - const response: Response = await fetch(url, { body, headers, method: 'POST' }); + const response = await (api?.url + ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) + : await createOpenaiAudioSpeechCompletion({ + openai: new OpenAI({ apiKey: key, baseURL: url }), + payload, + })); if (!response.ok) { throw new Error('Network response was not ok'); diff --git a/src/useAzureSpeech/demos/index.tsx b/src/useAzureSpeech/demos/index.tsx deleted file mode 100644 index 8d5e7b6..0000000 --- a/src/useAzureSpeech/demos/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { AudioPlayer, genLevaOptions, getAzureVoiceOptions, useAzureSpeech } from '@lobehub/tts'; -import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; -import { Button, Input } from 'antd'; -import { Volume2 } from 'lucide-react'; -import { Flexbox } from 'react-layout-kit'; - -const defaultText = '这是一段使用 Azure Speech 的语音演示'; - -export default () => { - const store = useCreateStore(); - - const api: any = useControls( - { - key: { - label: 'AZURE_SPEECH_KEY', - value: '', - }, - region: { - label: 'AZURE_SPEECH_REGION', - value: '', - }, - }, - { store }, - ); - - const options: any = useControls( - { - name: { - options: genLevaOptions(getAzureVoiceOptions()), - value: 'zh-CN-YunxiaNeural', - }, - pitch: { - max: 1, - min: -1, - step: 0.1, - value: 0, - }, - rate: { - max: 1, - min: -1, - step: 0.1, - value: 0, - }, - style: { - options: [ - 'affectionate', - 'angry', - 'calm', - 'cheerful', - 'disgruntled', - 'embarrassed', - 'fearful', - 'general', - 'gentle', - 'sad', - 'serious', - ], - value: 'general', - }, - }, - { store }, - ); - const { setText, isGlobalLoading, audio, start, stop } = useAzureSpeech(defaultText, { - api, - ...options, - }); - return ( - - - {isGlobalLoading ? ( - - ) : ( - - )} - setText(e.target.value)} /> - - - - ); -}; diff --git a/src/useAzureSpeech/index.md b/src/useAzureSpeech/index.md deleted file mode 100644 index 1bbc032..0000000 --- a/src/useAzureSpeech/index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -nav: Components -group: TTS -title: useAzureSpeech ---- - -## hooks - -- ENV: `AZURE_SPEECH_KEY` `AZURE_SPEECH_REGION` - - diff --git a/src/useAzureSpeech/index.ts b/src/useAzureSpeech/index.ts deleted file mode 100644 index 6e29ae5..0000000 --- a/src/useAzureSpeech/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useState } from 'react'; - -import { AzureSpeechOptions, fetchAzureSpeech } from '@/services/fetchAzureSpeech'; -import { useTTS } from '@/useTTS'; - -export const useAzureSpeech = (defaultText: string, options: AzureSpeechOptions) => { - const [text, setText] = useState(defaultText); - const rest = useTTS(options.name, text, (segmentText: string) => - fetchAzureSpeech(segmentText, options), - ); - return { - setText, - ...rest, - }; -}; diff --git a/src/useEdgeSpeech/demos/index.tsx b/src/useEdgeSpeech/demos/index.tsx index dae1ef8..f44e544 100644 --- a/src/useEdgeSpeech/demos/index.tsx +++ b/src/useEdgeSpeech/demos/index.tsx @@ -1,4 +1,10 @@ -import { AudioPlayer, genLevaOptions, getEdgeVoiceOptions, useEdgeSpeech } from '@lobehub/tts'; +import { + AudioPlayer, + EDGE_SPEECH_API_URL, + genLevaOptions, + getEdgeVoiceOptions, + useEdgeSpeech, +} from '@lobehub/tts'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; @@ -11,21 +17,14 @@ export default () => { const api: any = useControls( { - key: { - label: 'EDDGE_API_TOKEN', - value: '', - }, - proxy: { - label: 'EDDGE_PROXY_URL', - value: '', - }, + url: EDGE_SPEECH_API_URL, }, { store }, ); const options: any = useControls( { - name: { + voice: { options: genLevaOptions(getEdgeVoiceOptions()), value: 'zh-CN-YunxiaNeural', }, diff --git a/src/useEdgeSpeech/index.ts b/src/useEdgeSpeech/index.ts index ff2f7fc..6909b06 100644 --- a/src/useEdgeSpeech/index.ts +++ b/src/useEdgeSpeech/index.ts @@ -5,7 +5,7 @@ import { useTTS } from '@/useTTS'; export const useEdgeSpeech = (defaultText: string, options: EdgeSpeechOptions) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.name, text, (segmentText: string) => + const rest = useTTS(options.voice, text, (segmentText: string) => fetchEdgeSpeech(segmentText, options), ); return { diff --git a/src/useMicrosoftSpeech/demos/index.tsx b/src/useMicrosoftSpeech/demos/index.tsx index ac31eba..24cce4f 100644 --- a/src/useMicrosoftSpeech/demos/index.tsx +++ b/src/useMicrosoftSpeech/demos/index.tsx @@ -1,4 +1,10 @@ -import { AudioPlayer, genLevaOptions, getEdgeVoiceOptions, useMicrosoftSpeech } from '@lobehub/tts'; +import { + AudioPlayer, + MICROSOFT_SPEECH_API_URL, + genLevaOptions, + getEdgeVoiceOptions, + useMicrosoftSpeech, +} from '@lobehub/tts'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; @@ -8,16 +14,17 @@ const defaultText = '这是一段使用 Microsoft Speech 的语音演示'; export default () => { const store = useCreateStore(); - const options: any = useControls( + const api: any = useControls( { - api: { - label: 'MICROSOFT_SPEECH_PROXY_URL', - value: '', - }, - name: { - options: genLevaOptions(getEdgeVoiceOptions()), - value: 'zh-CN-YunxiaNeural', + url: { + label: 'MICROSOFT_SPEECH_API_URL', + value: MICROSOFT_SPEECH_API_URL, }, + }, + { store }, + ); + const options: any = useControls( + { pitch: { max: 1, min: -1, @@ -46,10 +53,17 @@ export default () => { ], value: 'general', }, + voice: { + options: genLevaOptions(getEdgeVoiceOptions()), + value: 'zh-CN-YunxiaNeural', + }, }, { store }, ); - const { setText, isGlobalLoading, audio, start, stop } = useMicrosoftSpeech(defaultText, options); + const { setText, isGlobalLoading, audio, start, stop } = useMicrosoftSpeech(defaultText, { + api, + ...options, + }); return ( diff --git a/src/useMicrosoftSpeech/index.md b/src/useMicrosoftSpeech/index.md index 8ab707e..b431580 100644 --- a/src/useMicrosoftSpeech/index.md +++ b/src/useMicrosoftSpeech/index.md @@ -6,6 +6,6 @@ title: useMicrosoftSpeech ## hooks -- ENV: `MICROSOFT_SPEECH_PROXY_URL` +- ENV: `MICROSOFT_SPEECH_API_URL` diff --git a/src/useMicrosoftSpeech/index.ts b/src/useMicrosoftSpeech/index.ts index 637d2c6..3e2e150 100644 --- a/src/useMicrosoftSpeech/index.ts +++ b/src/useMicrosoftSpeech/index.ts @@ -5,7 +5,7 @@ import { useTTS } from '@/useTTS'; export const useMicrosoftSpeech = (defaultText: string, options: MicrosoftSpeechOptions) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.name, text, (segmentText: string) => + const rest = useTTS(options.voice, text, (segmentText: string) => fetchMicrosoftSpeech(segmentText, options), ); return { diff --git a/src/useOpenaiSTT/demos/index.tsx b/src/useOpenaiSTT/demos/index.tsx index e638a02..3b05f8e 100644 --- a/src/useOpenaiSTT/demos/index.tsx +++ b/src/useOpenaiSTT/demos/index.tsx @@ -1,4 +1,4 @@ -import { useOpenaiSTTWithRecord } from '@lobehub/tts'; +import { OPENAI_STT_API_URL, useOpenaiSTTWithRecord } from '@lobehub/tts'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; @@ -16,6 +16,7 @@ export default () => { label: 'OPENAI_PROXY_URL', value: '', }, + url: OPENAI_STT_API_URL, }, { store }, ); diff --git a/src/useOpenaiTTS/demos/index.tsx b/src/useOpenaiTTS/demos/index.tsx index b07e534..cfbc24b 100644 --- a/src/useOpenaiTTS/demos/index.tsx +++ b/src/useOpenaiTTS/demos/index.tsx @@ -1,4 +1,4 @@ -import { AudioPlayer, openaiVoiceList, useOpenaiTTS } from '@lobehub/tts'; +import { AudioPlayer, OPENAI_TTS_API_URL, openaiVoiceList, useOpenaiTTS } from '@lobehub/tts'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; @@ -19,13 +19,14 @@ export default () => { label: 'OPENAI_PROXY_URL', value: '', }, + url: OPENAI_TTS_API_URL, }, { store }, ); const options: any = useControls( { - name: { + voice: { options: openaiVoiceList, value: 'alloy', }, diff --git a/src/useOpenaiTTS/index.ts b/src/useOpenaiTTS/index.ts index d04cc68..c88a78b 100644 --- a/src/useOpenaiTTS/index.ts +++ b/src/useOpenaiTTS/index.ts @@ -5,7 +5,7 @@ import { useTTS } from '@/useTTS'; export const useOpenaiTTS = (defaultText: string, options: OpenaiTtsOptions) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.name, text, (segmentText: string) => + const rest = useTTS(options.voice, text, (segmentText: string) => fetchOpenaiTTS(segmentText, options), ); return { diff --git a/src/useSpeechSynthes/demos/index.tsx b/src/useSpeechSynthes/demos/index.tsx index 162c9d6..f1406d5 100644 --- a/src/useSpeechSynthes/demos/index.tsx +++ b/src/useSpeechSynthes/demos/index.tsx @@ -10,10 +10,6 @@ export default () => { const store = useCreateStore(); const options: any = useControls( { - name: { - options: genLevaOptions(getSpeechSynthesVoiceOptions()), - value: '婷婷', - }, pitch: { max: 1, min: -1, @@ -26,6 +22,10 @@ export default () => { step: 0.1, value: 0, }, + voice: { + options: genLevaOptions(getSpeechSynthesVoiceOptions()), + value: '婷婷', + }, }, { store }, ); diff --git a/src/useSpeechSynthes/index.ts b/src/useSpeechSynthes/index.ts index e34cea2..af9b347 100644 --- a/src/useSpeechSynthes/index.ts +++ b/src/useSpeechSynthes/index.ts @@ -2,18 +2,18 @@ import { useCallback, useMemo, useState } from 'react'; import { SsmlOptions } from '@/utils/genSSML'; -export const useSpeechSynthes = (defaultText: string, { name, rate, pitch }: SsmlOptions) => { +export const useSpeechSynthes = (defaultText: string, { voice, rate, pitch }: SsmlOptions) => { const [voiceList, setVoiceList] = useState(speechSynthesis.getVoices()); const [text, setText] = useState(defaultText); const [isLoading, setIsLoading] = useState(false); const speechSynthesisUtterance = useMemo(() => { const utterance = new SpeechSynthesisUtterance(text); - utterance.voice = voiceList.find((item) => item.name === name) as any; + utterance.voice = voiceList.find((item) => item.name === voice) as any; if (pitch) utterance.pitch = pitch * 10; if (rate) utterance.rate = rate * 10; return utterance; - }, [text, voiceList, rate, pitch, name]); + }, [text, voiceList, rate, pitch, voice]); speechSynthesis.onvoiceschanged = () => { setVoiceList(speechSynthesis.getVoices()); diff --git a/src/utils/genSSML.ts b/src/utils/genSSML.ts index 1e0bc1e..57dd52a 100644 --- a/src/utils/genSSML.ts +++ b/src/utils/genSSML.ts @@ -1,5 +1,3 @@ -import { Document, ServiceProvider } from 'ssml-document'; - export type StyleName = | 'affectionate' | 'angry' @@ -14,16 +12,34 @@ export type StyleName = | 'serious'; export interface SsmlOptions { - name: string; pitch?: number; rate?: number; style?: StyleName; + voice: string; } -export const genSSML = (text: string, options: SsmlOptions) => { - let ssml = new Document().voice(options.name); - if (options.style) ssml.expressAs({ style: options.style }); - if (options.pitch || options.rate) ssml.prosody({ pitch: options.pitch, rate: options.rate }); - const result = ssml.say(text).render({ provider: ServiceProvider.Microsoft }); - return `${result}`; +const voiceTemplate = (input: string, { voice }: Pick) => + `${input}`; + +const styleTemplate = (input: string, { style }: Pick) => { + if (!style) return input; + return `${input}`; +}; + +const prosodyTemplate = (input: string, { pitch, rate }: Pick) => { + if (!pitch && !rate) return input; + return `${input}`; +}; +const speackTemplate = (input: string) => + `${input}`; + +export const genSSML = (input: string, options: SsmlOptions) => { + let ssml = prosodyTemplate(input, options); + ssml = styleTemplate(ssml, options); + ssml = voiceTemplate(ssml, options); + ssml = speackTemplate(ssml); + + return ssml; }; diff --git a/src/utils/getVoiceList.ts b/src/utils/getVoiceList.ts index 841b040..9cd789b 100644 --- a/src/utils/getVoiceList.ts +++ b/src/utils/getVoiceList.ts @@ -4,9 +4,9 @@ import { flatten } from 'lodash-es'; import azureVoiceList from '@/data/azureVoiceList'; import edgeVoiceList from '@/data/edgeVoiceList'; import voiceLocale from '@/data/locales'; -import nameList from '@/data/nameList'; import openaiVoiceList from '@/data/openaiVoiceList'; import speechSynthesVoiceList from '@/data/speechSynthesVoiceList'; +import voiceList from '@/data/voiceList'; export const genSpeechSynthesVoiceList = () => { const data = speechSynthesis.getVoices(); @@ -38,7 +38,7 @@ export const getAzureVoiceOptions = (locale?: string): SelectProps['options'] => ? (azureVoiceList as any)?.[locale] || [] : flatten(Object.values(azureVoiceList)); - return data.map((voice: any) => ({ label: (nameList as any)?.[voice] || voice, value: voice })); + return data.map((voice: any) => ({ label: (voiceList as any)?.[voice] || voice, value: voice })); }; export const getEdgeVoiceOptions = (locale?: string): SelectProps['options'] => { @@ -46,7 +46,7 @@ export const getEdgeVoiceOptions = (locale?: string): SelectProps['options'] => locale && (edgeVoiceList as any)[locale] ? (edgeVoiceList as any)[locale] || [] : flatten(Object.values(edgeVoiceList)); - return data.map((voice: any) => ({ label: (nameList as any)?.[voice] || voice, value: voice })); + return data.map((voice: any) => ({ label: (voiceList as any)?.[voice] || voice, value: voice })); }; export const getOpenaiVoiceOptions = (): SelectProps['options'] => { From 26a4ad7f9087cc612bed526d02bdd23441b84e05 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 06:59:01 +0000 Subject: [PATCH 02/24] :bookmark: chore(release): v1.12.1-beta.1 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.0...v1.12.1-beta.1) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: Refactor api.
Improvements and Fixes #### Code refactoring * **misc**: Refactor api ([a7c0095](https://github.com/lobehub/lobe-tts/commit/a7c0095))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 957cd37..239cf4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.0...v1.12.1-beta.1) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: Refactor api. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Refactor api ([a7c0095](https://github.com/lobehub/lobe-tts/commit/a7c0095)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ## [Version 1.12.0](https://github.com/lobehub/lobe-tts/compare/v1.11.1...v1.12.0) Released on **2023-11-13** diff --git a/package.json b/package.json index ca9845d..ced8799 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.0", + "version": "1.12.1-beta.1", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 80b24e8584a4abc518dc11f8917e287cdb87d65c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 14:55:06 +0800 Subject: [PATCH 03/24] =?UTF-8?q?:recycle:=20refactor:=20=E5=B0=86react=20?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=8A=BD=E5=8F=96=E5=88=B0=20/react=20?= =?UTF-8?q?=E5=AD=90=E7=BA=A7=E8=B7=AF=E5=BE=84=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dumirc.ts | 9 +++--- package.json | 13 +++++++++ src/index.ts | 29 ++++--------------- src/react.ts | 24 +++++++++++++++ src/{ => react}/AudioPlayer/demos/index.tsx | 2 +- src/{ => react}/AudioPlayer/index.md | 0 src/{ => react}/AudioPlayer/index.tsx | 0 .../AudioVisualizer/Visualizer.tsx | 3 +- .../AudioVisualizer/demos/index.tsx | 2 +- src/{ => react}/AudioVisualizer/index.md | 0 src/{ => react}/AudioVisualizer/index.tsx | 2 +- src/{ => react}/hooks/useAudioPlayer.ts | 2 +- src/{ => react}/hooks/useAudioVisualizer.ts | 0 src/{ => react}/hooks/useBlobUrl.ts | 0 src/{ => react}/hooks/useStreamAudioPlayer.ts | 2 +- .../useAudioRecorder/demos/index.tsx | 2 +- src/{ => react}/useAudioRecorder/index.md | 0 src/{ => react}/useAudioRecorder/index.ts | 0 src/{ => react}/useEdgeSpeech/demos/index.tsx | 9 ++---- src/{ => react}/useEdgeSpeech/index.md | 0 src/{ => react}/useEdgeSpeech/index.ts | 2 +- .../useMicrosoftSpeech/demos/index.tsx | 9 ++---- src/{ => react}/useMicrosoftSpeech/index.md | 0 src/{ => react}/useMicrosoftSpeech/index.ts | 2 +- .../useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx | 2 +- .../useOpenaiSTT/demos/OpenaiSTTWithSR.tsx | 2 +- src/{ => react}/useOpenaiSTT/demos/index.tsx | 3 +- src/{ => react}/useOpenaiSTT/index.md | 0 src/{ => react}/useOpenaiSTT/index.ts | 0 src/{ => react}/useOpenaiSTT/useOpenaiSTT.ts | 0 .../useOpenaiSTT/useOpenaiSTTWithPSR.ts | 4 +-- .../useOpenaiSTT/useOpenaiSTTWithRecord.ts | 6 ++-- .../useOpenaiSTT/useOpenaiSTTWithSR.ts | 4 +-- src/{ => react}/useOpenaiTTS/demos/index.tsx | 3 +- src/{ => react}/useOpenaiTTS/index.md | 0 src/{ => react}/useOpenaiTTS/index.ts | 2 +- .../demos/PersistedSpeechRecognition.tsx | 2 +- .../useSpeechRecognition/demos/index.tsx | 2 +- src/{ => react}/useSpeechRecognition/index.md | 0 src/{ => react}/useSpeechRecognition/index.ts | 0 .../usePersistedSpeechRecognition.ts | 2 +- .../useSpeechRecognition/useRecognition.ts | 0 .../useSpeechRecognition.ts | 4 +-- .../useSpeechSynthes/demos/index.tsx | 3 +- src/{ => react}/useSpeechSynthes/index.md | 0 src/{ => react}/useSpeechSynthes/index.ts | 0 src/{ => react}/useTTS/index.ts | 4 +-- src/server.ts | 5 ---- 48 files changed, 83 insertions(+), 77 deletions(-) create mode 100644 src/react.ts rename src/{ => react}/AudioPlayer/demos/index.tsx (91%) rename src/{ => react}/AudioPlayer/index.md (100%) rename src/{ => react}/AudioPlayer/index.tsx (100%) rename src/{ => react}/AudioVisualizer/Visualizer.tsx (93%) rename src/{ => react}/AudioVisualizer/demos/index.tsx (98%) rename src/{ => react}/AudioVisualizer/index.md (100%) rename src/{ => react}/AudioVisualizer/index.tsx (94%) rename src/{ => react}/hooks/useAudioPlayer.ts (98%) rename src/{ => react}/hooks/useAudioVisualizer.ts (100%) rename src/{ => react}/hooks/useBlobUrl.ts (100%) rename src/{ => react}/hooks/useStreamAudioPlayer.ts (98%) rename src/{ => react}/useAudioRecorder/demos/index.tsx (92%) rename src/{ => react}/useAudioRecorder/index.md (100%) rename src/{ => react}/useAudioRecorder/index.ts (100%) rename src/{ => react}/useEdgeSpeech/demos/index.tsx (89%) rename src/{ => react}/useEdgeSpeech/index.md (100%) rename src/{ => react}/useEdgeSpeech/index.ts (91%) rename src/{ => react}/useMicrosoftSpeech/demos/index.tsx (91%) rename src/{ => react}/useMicrosoftSpeech/index.md (100%) rename src/{ => react}/useMicrosoftSpeech/index.ts (91%) rename src/{ => react}/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx (95%) rename src/{ => react}/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx (95%) rename src/{ => react}/useOpenaiSTT/demos/index.tsx (91%) rename src/{ => react}/useOpenaiSTT/index.md (100%) rename src/{ => react}/useOpenaiSTT/index.ts (100%) rename src/{ => react}/useOpenaiSTT/useOpenaiSTT.ts (100%) rename src/{ => react}/useOpenaiSTT/useOpenaiSTTWithPSR.ts (90%) rename src/{ => react}/useOpenaiSTT/useOpenaiSTTWithRecord.ts (86%) rename src/{ => react}/useOpenaiSTT/useOpenaiSTTWithSR.ts (91%) rename src/{ => react}/useOpenaiTTS/demos/index.tsx (91%) rename src/{ => react}/useOpenaiTTS/index.md (100%) rename src/{ => react}/useOpenaiTTS/index.ts (91%) rename src/{ => react}/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx (93%) rename src/{ => react}/useSpeechRecognition/demos/index.tsx (94%) rename src/{ => react}/useSpeechRecognition/index.md (100%) rename src/{ => react}/useSpeechRecognition/index.ts (100%) rename src/{ => react}/useSpeechRecognition/usePersistedSpeechRecognition.ts (95%) rename src/{ => react}/useSpeechRecognition/useRecognition.ts (100%) rename src/{ => react}/useSpeechRecognition/useSpeechRecognition.ts (86%) rename src/{ => react}/useSpeechSynthes/demos/index.tsx (90%) rename src/{ => react}/useSpeechSynthes/index.md (100%) rename src/{ => react}/useSpeechSynthes/index.ts (100%) rename src/{ => react}/useTTS/index.ts (93%) delete mode 100644 src/server.ts diff --git a/.dumirc.ts b/.dumirc.ts index 4e5e797..3785f0f 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -51,11 +51,10 @@ export default defineConfig({ mfsu: isWin ? undefined : {}, npmClient: 'pnpm', publicPath: '/', - resolve: isProduction - ? { - entryFile: './src/index.ts', - } - : undefined, + resolve: { + atomDirs: [{ dir: 'src/react', type: 'component' }], + entryFile: isProduction ? './src/index.ts' : undefined, + }, styles: [ `html, body { background: transparent; } diff --git a/package.json b/package.json index ced8799..50815c6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,19 @@ "license": "MIT", "author": "LobeHub ", "sideEffects": false, + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./es/index.d.ts", + "import": "./es/index.js", + "module": "./es/index.js" + }, + "./react": { + "types": "./es/react/index.d.ts", + "import": "./es/react/index.js", + "module": "./es/react/index.js" + } + }, "main": "es/index.js", "module": "es/index.js", "types": "es/index.d.ts", diff --git a/src/index.ts b/src/index.ts index 985450d..c819e1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,36 +1,12 @@ -export { default as AudioPlayer, type AudioPlayerProps } from './AudioPlayer'; -export { default as AudioVisualizer, type AudioVisualizerProps } from './AudioVisualizer'; export { default as azureVoiceList } from './data/azureVoiceList'; export { default as edgeVoiceList } from './data/edgeVoiceList'; export { default as voiceLocale } from './data/locales'; export { default as openaiVoiceList } from './data/openaiVoiceList'; export { default as voiceList } from './data/voiceList'; -export { useAudioPlayer } from './hooks/useAudioPlayer'; -export { useAudioVisualizer } from './hooks/useAudioVisualizer'; -export { useBlobUrl } from './hooks/useBlobUrl'; -export { useStreamAudioPlayer } from './hooks/useStreamAudioPlayer'; export { type EdgeSpeechOptions, fetchEdgeSpeech } from './services/fetchEdgeSpeech'; export { fetchMicrosoftSpeech, type MicrosoftSpeechOptions } from './services/fetchMicrosoftSpeech'; export { fetchOpenaiSTT, type OpenaiSttOptions } from './services/fetchOpenaiSTT'; export { fetchOpenaiTTS, type OpenaiTtsOptions } from './services/fetchOpenaiTTS'; -export { useAudioRecorder } from './useAudioRecorder'; -export { useEdgeSpeech } from './useEdgeSpeech'; -export { useMicrosoftSpeech } from './useMicrosoftSpeech'; -export { - type OpenaiSpeechRecognitionOptions, - type OpenaiSTTFetcher, - useOpenaiSTT, - useOpenaiSTTWithPSR, - useOpenaiSTTWithRecord, - useOpenaiSTTWithSR, -} from './useOpenaiSTT'; -export { useOpenaiTTS } from './useOpenaiTTS'; -export { usePersistedSpeechRecognition } from './useSpeechRecognition/usePersistedSpeechRecognition'; -export { - type SpeechRecognitionOptions, - useSpeechRecognition, -} from './useSpeechRecognition/useSpeechRecognition'; -export { useSpeechSynthes } from './useSpeechSynthes'; export { getRecordMineType, type RecordMineType } from './utils/getRecordMineType'; export { genLevaOptions, @@ -46,3 +22,8 @@ export { OPENAI_STT_API_URL, OPENAI_TTS_API_URL, } from '@/const/api'; +export { createEdgeSpeechComletion } from '@/server/createEdgeSpeechComletion'; +export { createMicrosoftSpeechComletion } from '@/server/createMicrosoftSpeechComletion'; +export { createOpenaiAudioSpeechCompletion } from '@/server/createOpenaiAudioSpeechCompletion'; +export { createOpenaiAudioTranscriptionsCompletion } from '@/server/createOpenaiAudioTranscriptionsCompletion'; +export * from '@/server/types'; diff --git a/src/react.ts b/src/react.ts new file mode 100644 index 0000000..5e6380e --- /dev/null +++ b/src/react.ts @@ -0,0 +1,24 @@ +export { default as AudioPlayer, type AudioPlayerProps } from '@/react/AudioPlayer'; +export { default as AudioVisualizer, type AudioVisualizerProps } from '@/react/AudioVisualizer'; +export { useAudioPlayer } from '@/react/hooks/useAudioPlayer'; +export { useAudioVisualizer } from '@/react/hooks/useAudioVisualizer'; +export { useBlobUrl } from '@/react/hooks/useBlobUrl'; +export { useStreamAudioPlayer } from '@/react/hooks/useStreamAudioPlayer'; +export { useAudioRecorder } from '@/react/useAudioRecorder'; +export { useEdgeSpeech } from '@/react/useEdgeSpeech'; +export { useMicrosoftSpeech } from '@/react/useMicrosoftSpeech'; +export { + type OpenaiSpeechRecognitionOptions, + type OpenaiSTTFetcher, + useOpenaiSTT, + useOpenaiSTTWithPSR, + useOpenaiSTTWithRecord, + useOpenaiSTTWithSR, +} from '@/react/useOpenaiSTT'; +export { useOpenaiTTS } from '@/react/useOpenaiTTS'; +export { usePersistedSpeechRecognition } from '@/react/useSpeechRecognition/usePersistedSpeechRecognition'; +export { + type SpeechRecognitionOptions, + useSpeechRecognition, +} from '@/react/useSpeechRecognition/useSpeechRecognition'; +export { useSpeechSynthes } from '@/react/useSpeechSynthes'; diff --git a/src/AudioPlayer/demos/index.tsx b/src/react/AudioPlayer/demos/index.tsx similarity index 91% rename from src/AudioPlayer/demos/index.tsx rename to src/react/AudioPlayer/demos/index.tsx index 0310eb6..3bc6309 100644 --- a/src/AudioPlayer/demos/index.tsx +++ b/src/react/AudioPlayer/demos/index.tsx @@ -1,4 +1,4 @@ -import { AudioPlayer, useAudioPlayer } from '@lobehub/tts'; +import { AudioPlayer, useAudioPlayer } from '@lobehub/tts/react'; import { StoryBook, useControls, useCreateStore } from '@lobehub/ui'; export default () => { diff --git a/src/AudioPlayer/index.md b/src/react/AudioPlayer/index.md similarity index 100% rename from src/AudioPlayer/index.md rename to src/react/AudioPlayer/index.md diff --git a/src/AudioPlayer/index.tsx b/src/react/AudioPlayer/index.tsx similarity index 100% rename from src/AudioPlayer/index.tsx rename to src/react/AudioPlayer/index.tsx diff --git a/src/AudioVisualizer/Visualizer.tsx b/src/react/AudioVisualizer/Visualizer.tsx similarity index 93% rename from src/AudioVisualizer/Visualizer.tsx rename to src/react/AudioVisualizer/Visualizer.tsx index 0fa7e65..dd889c2 100644 --- a/src/AudioVisualizer/Visualizer.tsx +++ b/src/react/AudioVisualizer/Visualizer.tsx @@ -1,7 +1,8 @@ -import { useAudioVisualizer } from '@lobehub/tts'; import { useTheme } from 'antd-style'; import { RefObject, memo } from 'react'; +import { useAudioVisualizer } from '../hooks/useAudioVisualizer'; + export interface VisualizerProps { borderRadius?: number; color?: string; diff --git a/src/AudioVisualizer/demos/index.tsx b/src/react/AudioVisualizer/demos/index.tsx similarity index 98% rename from src/AudioVisualizer/demos/index.tsx rename to src/react/AudioVisualizer/demos/index.tsx index d6d67b8..d195e03 100644 --- a/src/AudioVisualizer/demos/index.tsx +++ b/src/react/AudioVisualizer/demos/index.tsx @@ -1,4 +1,4 @@ -import { AudioPlayer, AudioVisualizer, useAudioPlayer } from '@lobehub/tts'; +import { AudioPlayer, AudioVisualizer, useAudioPlayer } from '@lobehub/tts/react'; import { StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Flexbox } from 'react-layout-kit'; diff --git a/src/AudioVisualizer/index.md b/src/react/AudioVisualizer/index.md similarity index 100% rename from src/AudioVisualizer/index.md rename to src/react/AudioVisualizer/index.md diff --git a/src/AudioVisualizer/index.tsx b/src/react/AudioVisualizer/index.tsx similarity index 94% rename from src/AudioVisualizer/index.tsx rename to src/react/AudioVisualizer/index.tsx index 2e80d1b..b8de105 100644 --- a/src/AudioVisualizer/index.tsx +++ b/src/react/AudioVisualizer/index.tsx @@ -4,7 +4,7 @@ import { CSSProperties, RefObject, memo } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { Flexbox } from 'react-layout-kit'; -import Visualizer, { VisualizerProps } from '@/AudioVisualizer/Visualizer'; +import Visualizer, { VisualizerProps } from '@/react/AudioVisualizer/Visualizer'; export interface AudioVisualizerProps { audioRef: RefObject; diff --git a/src/hooks/useAudioPlayer.ts b/src/react/hooks/useAudioPlayer.ts similarity index 98% rename from src/hooks/useAudioPlayer.ts rename to src/react/hooks/useAudioPlayer.ts index 66d2867..2bb7390 100644 --- a/src/hooks/useAudioPlayer.ts +++ b/src/react/hooks/useAudioPlayer.ts @@ -1,7 +1,7 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; import useSWR from 'swr'; -import { AudioProps } from '@/AudioPlayer'; +import { AudioProps } from '@/react/AudioPlayer'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; import { audioBufferToBlob } from '@/utils/audioBufferToBlob'; diff --git a/src/hooks/useAudioVisualizer.ts b/src/react/hooks/useAudioVisualizer.ts similarity index 100% rename from src/hooks/useAudioVisualizer.ts rename to src/react/hooks/useAudioVisualizer.ts diff --git a/src/hooks/useBlobUrl.ts b/src/react/hooks/useBlobUrl.ts similarity index 100% rename from src/hooks/useBlobUrl.ts rename to src/react/hooks/useBlobUrl.ts diff --git a/src/hooks/useStreamAudioPlayer.ts b/src/react/hooks/useStreamAudioPlayer.ts similarity index 98% rename from src/hooks/useStreamAudioPlayer.ts rename to src/react/hooks/useStreamAudioPlayer.ts index 07b1d7a..224c296 100644 --- a/src/hooks/useStreamAudioPlayer.ts +++ b/src/react/hooks/useStreamAudioPlayer.ts @@ -1,6 +1,6 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; -import { AudioProps } from '@/AudioPlayer'; +import { AudioProps } from '@/react/AudioPlayer'; import { audioBufferToBlob, audioBuffersToBlob } from '@/utils/audioBufferToBlob'; export interface StreamAudioPlayerHook extends AudioProps { diff --git a/src/useAudioRecorder/demos/index.tsx b/src/react/useAudioRecorder/demos/index.tsx similarity index 92% rename from src/useAudioRecorder/demos/index.tsx rename to src/react/useAudioRecorder/demos/index.tsx index d9b8442..40e3c9e 100644 --- a/src/useAudioRecorder/demos/index.tsx +++ b/src/react/useAudioRecorder/demos/index.tsx @@ -1,4 +1,4 @@ -import { useAudioRecorder } from '@lobehub/tts'; +import { useAudioRecorder } from '@lobehub/tts/react'; import { Icon } from '@lobehub/ui'; import { Button } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useAudioRecorder/index.md b/src/react/useAudioRecorder/index.md similarity index 100% rename from src/useAudioRecorder/index.md rename to src/react/useAudioRecorder/index.md diff --git a/src/useAudioRecorder/index.ts b/src/react/useAudioRecorder/index.ts similarity index 100% rename from src/useAudioRecorder/index.ts rename to src/react/useAudioRecorder/index.ts diff --git a/src/useEdgeSpeech/demos/index.tsx b/src/react/useEdgeSpeech/demos/index.tsx similarity index 89% rename from src/useEdgeSpeech/demos/index.tsx rename to src/react/useEdgeSpeech/demos/index.tsx index f44e544..eefc251 100644 --- a/src/useEdgeSpeech/demos/index.tsx +++ b/src/react/useEdgeSpeech/demos/index.tsx @@ -1,10 +1,5 @@ -import { - AudioPlayer, - EDGE_SPEECH_API_URL, - genLevaOptions, - getEdgeVoiceOptions, - useEdgeSpeech, -} from '@lobehub/tts'; +import { EDGE_SPEECH_API_URL, genLevaOptions, getEdgeVoiceOptions } from '@lobehub/tts'; +import { AudioPlayer, useEdgeSpeech } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; diff --git a/src/useEdgeSpeech/index.md b/src/react/useEdgeSpeech/index.md similarity index 100% rename from src/useEdgeSpeech/index.md rename to src/react/useEdgeSpeech/index.md diff --git a/src/useEdgeSpeech/index.ts b/src/react/useEdgeSpeech/index.ts similarity index 91% rename from src/useEdgeSpeech/index.ts rename to src/react/useEdgeSpeech/index.ts index 6909b06..bc76f57 100644 --- a/src/useEdgeSpeech/index.ts +++ b/src/react/useEdgeSpeech/index.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; +import { useTTS } from '@/react/useTTS'; import { EdgeSpeechOptions, fetchEdgeSpeech } from '@/services/fetchEdgeSpeech'; -import { useTTS } from '@/useTTS'; export const useEdgeSpeech = (defaultText: string, options: EdgeSpeechOptions) => { const [text, setText] = useState(defaultText); diff --git a/src/useMicrosoftSpeech/demos/index.tsx b/src/react/useMicrosoftSpeech/demos/index.tsx similarity index 91% rename from src/useMicrosoftSpeech/demos/index.tsx rename to src/react/useMicrosoftSpeech/demos/index.tsx index 24cce4f..a93254f 100644 --- a/src/useMicrosoftSpeech/demos/index.tsx +++ b/src/react/useMicrosoftSpeech/demos/index.tsx @@ -1,10 +1,5 @@ -import { - AudioPlayer, - MICROSOFT_SPEECH_API_URL, - genLevaOptions, - getEdgeVoiceOptions, - useMicrosoftSpeech, -} from '@lobehub/tts'; +import { MICROSOFT_SPEECH_API_URL, genLevaOptions, getEdgeVoiceOptions } from '@lobehub/tts'; +import { AudioPlayer, useMicrosoftSpeech } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; diff --git a/src/useMicrosoftSpeech/index.md b/src/react/useMicrosoftSpeech/index.md similarity index 100% rename from src/useMicrosoftSpeech/index.md rename to src/react/useMicrosoftSpeech/index.md diff --git a/src/useMicrosoftSpeech/index.ts b/src/react/useMicrosoftSpeech/index.ts similarity index 91% rename from src/useMicrosoftSpeech/index.ts rename to src/react/useMicrosoftSpeech/index.ts index 3e2e150..691fcfd 100644 --- a/src/useMicrosoftSpeech/index.ts +++ b/src/react/useMicrosoftSpeech/index.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; +import { useTTS } from '@/react/useTTS'; import { type MicrosoftSpeechOptions, fetchMicrosoftSpeech } from '@/services/fetchMicrosoftSpeech'; -import { useTTS } from '@/useTTS'; export const useMicrosoftSpeech = (defaultText: string, options: MicrosoftSpeechOptions) => { const [text, setText] = useState(defaultText); diff --git a/src/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx b/src/react/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx similarity index 95% rename from src/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx rename to src/react/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx index 88c15e4..3ff7ec3 100644 --- a/src/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx +++ b/src/react/useOpenaiSTT/demos/OpenaiSTTWithPSR.tsx @@ -1,4 +1,4 @@ -import { useOpenaiSTTWithPSR } from '@lobehub/tts'; +import { useOpenaiSTTWithPSR } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx b/src/react/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx similarity index 95% rename from src/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx rename to src/react/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx index 6cfd7c6..3103aac 100644 --- a/src/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx +++ b/src/react/useOpenaiSTT/demos/OpenaiSTTWithSR.tsx @@ -1,4 +1,4 @@ -import { useOpenaiSTTWithSR } from '@lobehub/tts'; +import { useOpenaiSTTWithSR } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useOpenaiSTT/demos/index.tsx b/src/react/useOpenaiSTT/demos/index.tsx similarity index 91% rename from src/useOpenaiSTT/demos/index.tsx rename to src/react/useOpenaiSTT/demos/index.tsx index 3b05f8e..aa1b687 100644 --- a/src/useOpenaiSTT/demos/index.tsx +++ b/src/react/useOpenaiSTT/demos/index.tsx @@ -1,4 +1,5 @@ -import { OPENAI_STT_API_URL, useOpenaiSTTWithRecord } from '@lobehub/tts'; +import { OPENAI_STT_API_URL } from '@lobehub/tts'; +import { useOpenaiSTTWithRecord } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useOpenaiSTT/index.md b/src/react/useOpenaiSTT/index.md similarity index 100% rename from src/useOpenaiSTT/index.md rename to src/react/useOpenaiSTT/index.md diff --git a/src/useOpenaiSTT/index.ts b/src/react/useOpenaiSTT/index.ts similarity index 100% rename from src/useOpenaiSTT/index.ts rename to src/react/useOpenaiSTT/index.ts diff --git a/src/useOpenaiSTT/useOpenaiSTT.ts b/src/react/useOpenaiSTT/useOpenaiSTT.ts similarity index 100% rename from src/useOpenaiSTT/useOpenaiSTT.ts rename to src/react/useOpenaiSTT/useOpenaiSTT.ts diff --git a/src/useOpenaiSTT/useOpenaiSTTWithPSR.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts similarity index 90% rename from src/useOpenaiSTT/useOpenaiSTTWithPSR.ts rename to src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts index 72b65ff..5dc31bd 100644 --- a/src/useOpenaiSTT/useOpenaiSTTWithPSR.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/useOpenaiSTT/useOpenaiSTT'; -import { usePersistedSpeechRecognition } from '@/useSpeechRecognition'; +import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { usePersistedSpeechRecognition } from '@/react/useSpeechRecognition'; import { OpenaiSpeechRecognitionOptions } from './useOpenaiSTTWithRecord'; diff --git a/src/useOpenaiSTT/useOpenaiSTTWithRecord.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts similarity index 86% rename from src/useOpenaiSTT/useOpenaiSTTWithRecord.ts rename to src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts index abc037f..820efb5 100644 --- a/src/useOpenaiSTT/useOpenaiSTTWithRecord.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts @@ -1,9 +1,9 @@ import { useCallback, useState } from 'react'; +import { useAudioRecorder } from '@/react/useAudioRecorder'; +import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { SpeechRecognitionOptions } from '@/react/useSpeechRecognition/useSpeechRecognition'; import { OpenaiSttOptions } from '@/services/fetchOpenaiSTT'; -import { useAudioRecorder } from '@/useAudioRecorder'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/useOpenaiSTT/useOpenaiSTT'; -import { SpeechRecognitionOptions } from '@/useSpeechRecognition/useSpeechRecognition'; export type OpenaiSpeechRecognitionOptions = SpeechRecognitionOptions & OpenaiSttOptions; diff --git a/src/useOpenaiSTT/useOpenaiSTTWithSR.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts similarity index 91% rename from src/useOpenaiSTT/useOpenaiSTTWithSR.ts rename to src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts index 6e92f74..8939c1a 100644 --- a/src/useOpenaiSTT/useOpenaiSTTWithSR.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/useOpenaiSTT/useOpenaiSTT'; -import { useSpeechRecognition } from '@/useSpeechRecognition'; +import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { useSpeechRecognition } from '@/react/useSpeechRecognition'; import { OpenaiSpeechRecognitionOptions } from './useOpenaiSTTWithRecord'; diff --git a/src/useOpenaiTTS/demos/index.tsx b/src/react/useOpenaiTTS/demos/index.tsx similarity index 91% rename from src/useOpenaiTTS/demos/index.tsx rename to src/react/useOpenaiTTS/demos/index.tsx index cfbc24b..8d923d1 100644 --- a/src/useOpenaiTTS/demos/index.tsx +++ b/src/react/useOpenaiTTS/demos/index.tsx @@ -1,4 +1,5 @@ -import { AudioPlayer, OPENAI_TTS_API_URL, openaiVoiceList, useOpenaiTTS } from '@lobehub/tts'; +import { OPENAI_TTS_API_URL, openaiVoiceList } from '@lobehub/tts'; +import { AudioPlayer, useOpenaiTTS } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; diff --git a/src/useOpenaiTTS/index.md b/src/react/useOpenaiTTS/index.md similarity index 100% rename from src/useOpenaiTTS/index.md rename to src/react/useOpenaiTTS/index.md diff --git a/src/useOpenaiTTS/index.ts b/src/react/useOpenaiTTS/index.ts similarity index 91% rename from src/useOpenaiTTS/index.ts rename to src/react/useOpenaiTTS/index.ts index c88a78b..74ae72a 100644 --- a/src/useOpenaiTTS/index.ts +++ b/src/react/useOpenaiTTS/index.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; +import { useTTS } from '@/react/useTTS'; import { type OpenaiTtsOptions, fetchOpenaiTTS } from '@/services/fetchOpenaiTTS'; -import { useTTS } from '@/useTTS'; export const useOpenaiTTS = (defaultText: string, options: OpenaiTtsOptions) => { const [text, setText] = useState(defaultText); diff --git a/src/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx b/src/react/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx similarity index 93% rename from src/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx rename to src/react/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx index 5b0400c..57cf7c6 100644 --- a/src/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx +++ b/src/react/useSpeechRecognition/demos/PersistedSpeechRecognition.tsx @@ -1,4 +1,4 @@ -import { usePersistedSpeechRecognition } from '@lobehub/tts'; +import { usePersistedSpeechRecognition } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useSpeechRecognition/demos/index.tsx b/src/react/useSpeechRecognition/demos/index.tsx similarity index 94% rename from src/useSpeechRecognition/demos/index.tsx rename to src/react/useSpeechRecognition/demos/index.tsx index 59ff68c..f361c38 100644 --- a/src/useSpeechRecognition/demos/index.tsx +++ b/src/react/useSpeechRecognition/demos/index.tsx @@ -1,4 +1,4 @@ -import { useSpeechRecognition } from '@lobehub/tts'; +import { useSpeechRecognition } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Mic, StopCircle } from 'lucide-react'; diff --git a/src/useSpeechRecognition/index.md b/src/react/useSpeechRecognition/index.md similarity index 100% rename from src/useSpeechRecognition/index.md rename to src/react/useSpeechRecognition/index.md diff --git a/src/useSpeechRecognition/index.ts b/src/react/useSpeechRecognition/index.ts similarity index 100% rename from src/useSpeechRecognition/index.ts rename to src/react/useSpeechRecognition/index.ts diff --git a/src/useSpeechRecognition/usePersistedSpeechRecognition.ts b/src/react/useSpeechRecognition/usePersistedSpeechRecognition.ts similarity index 95% rename from src/useSpeechRecognition/usePersistedSpeechRecognition.ts rename to src/react/useSpeechRecognition/usePersistedSpeechRecognition.ts index f2bd740..6b0e385 100644 --- a/src/useSpeechRecognition/usePersistedSpeechRecognition.ts +++ b/src/react/useSpeechRecognition/usePersistedSpeechRecognition.ts @@ -1,6 +1,6 @@ import { useCallback, useMemo, useState } from 'react'; -import { useAudioRecorder } from '@/useAudioRecorder'; +import { useAudioRecorder } from '@/react/useAudioRecorder'; import { useRecognition } from './useRecognition'; import { SpeechRecognitionOptions } from './useSpeechRecognition'; diff --git a/src/useSpeechRecognition/useRecognition.ts b/src/react/useSpeechRecognition/useRecognition.ts similarity index 100% rename from src/useSpeechRecognition/useRecognition.ts rename to src/react/useSpeechRecognition/useRecognition.ts diff --git a/src/useSpeechRecognition/useSpeechRecognition.ts b/src/react/useSpeechRecognition/useSpeechRecognition.ts similarity index 86% rename from src/useSpeechRecognition/useSpeechRecognition.ts rename to src/react/useSpeechRecognition/useSpeechRecognition.ts index 285b666..8e80c93 100644 --- a/src/useSpeechRecognition/useSpeechRecognition.ts +++ b/src/react/useSpeechRecognition/useSpeechRecognition.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; -import { useAudioRecorder } from '@/useAudioRecorder'; -import { useRecognition } from '@/useSpeechRecognition/useRecognition'; +import { useAudioRecorder } from '@/react/useAudioRecorder'; +import { useRecognition } from '@/react/useSpeechRecognition/useRecognition'; export interface SpeechRecognitionOptions { onBolbAvailable?: (blob: Blob) => void; diff --git a/src/useSpeechSynthes/demos/index.tsx b/src/react/useSpeechSynthes/demos/index.tsx similarity index 90% rename from src/useSpeechSynthes/demos/index.tsx rename to src/react/useSpeechSynthes/demos/index.tsx index f1406d5..8eba110 100644 --- a/src/useSpeechSynthes/demos/index.tsx +++ b/src/react/useSpeechSynthes/demos/index.tsx @@ -1,4 +1,5 @@ -import { genLevaOptions, getSpeechSynthesVoiceOptions, useSpeechSynthes } from '@lobehub/tts'; +import { genLevaOptions, getSpeechSynthesVoiceOptions } from '@lobehub/tts'; +import { useSpeechSynthes } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { StopCircle, Volume2 } from 'lucide-react'; diff --git a/src/useSpeechSynthes/index.md b/src/react/useSpeechSynthes/index.md similarity index 100% rename from src/useSpeechSynthes/index.md rename to src/react/useSpeechSynthes/index.md diff --git a/src/useSpeechSynthes/index.ts b/src/react/useSpeechSynthes/index.ts similarity index 100% rename from src/useSpeechSynthes/index.ts rename to src/react/useSpeechSynthes/index.ts diff --git a/src/useTTS/index.ts b/src/react/useTTS/index.ts similarity index 93% rename from src/useTTS/index.ts rename to src/react/useTTS/index.ts index b548b24..a922fcb 100644 --- a/src/useTTS/index.ts +++ b/src/react/useTTS/index.ts @@ -1,8 +1,8 @@ import { useCallback, useEffect, useState } from 'react'; import useSWR from 'swr'; -import { AudioProps } from '@/AudioPlayer'; -import { useStreamAudioPlayer } from '@/hooks/useStreamAudioPlayer'; +import { AudioProps } from '@/react/AudioPlayer'; +import { useStreamAudioPlayer } from '@/react/hooks/useStreamAudioPlayer'; import { splitTextIntoSegments } from '@/utils/splitTextIntoSegments'; export interface TTSHook { diff --git a/src/server.ts b/src/server.ts deleted file mode 100644 index 114b555..0000000 --- a/src/server.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { createEdgeSpeechComletion } from '@/server/createEdgeSpeechComletion'; -export { createMicrosoftSpeechComletion } from '@/server/createMicrosoftSpeechComletion'; -export { createOpenaiAudioSpeechCompletion } from '@/server/createOpenaiAudioSpeechCompletion'; -export { createOpenaiAudioTranscriptionsCompletion } from '@/server/createOpenaiAudioTranscriptionsCompletion'; -export * from '@/server/types'; From cdcb9ccd42e07dbe71b955e937b3cc6ffb915e16 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 08:08:37 +0000 Subject: [PATCH 04/24] :bookmark: chore(release): v1.12.1-beta.2 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.1...v1.12.1-beta.2) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: 将 react 部分抽取到 /react 子级路径下.
Improvements and Fixes #### Code refactoring * **misc**: 将 react 部分抽取到 /react 子级路径下 ([80b24e8](https://github.com/lobehub/lobe-tts/commit/80b24e8))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 239cf4c..ac47f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.1...v1.12.1-beta.2) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: 将 react 部分抽取到 /react 子级路径下. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: 将 react 部分抽取到 /react 子级路径下 ([80b24e8](https://github.com/lobehub/lobe-tts/commit/80b24e8)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.0...v1.12.1-beta.1) Released on **2023-11-15** diff --git a/package.json b/package.json index 50815c6..9401975 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.1", + "version": "1.12.1-beta.2", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 6a4631d582fa203891ca43f813e54ce3411fd076 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 18:03:15 +0800 Subject: [PATCH 05/24] :recycle: refactor: fix react entry --- src/react.ts | 24 ------------------------ src/react/index.ts | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 24 deletions(-) delete mode 100644 src/react.ts create mode 100644 src/react/index.ts diff --git a/src/react.ts b/src/react.ts deleted file mode 100644 index 5e6380e..0000000 --- a/src/react.ts +++ /dev/null @@ -1,24 +0,0 @@ -export { default as AudioPlayer, type AudioPlayerProps } from '@/react/AudioPlayer'; -export { default as AudioVisualizer, type AudioVisualizerProps } from '@/react/AudioVisualizer'; -export { useAudioPlayer } from '@/react/hooks/useAudioPlayer'; -export { useAudioVisualizer } from '@/react/hooks/useAudioVisualizer'; -export { useBlobUrl } from '@/react/hooks/useBlobUrl'; -export { useStreamAudioPlayer } from '@/react/hooks/useStreamAudioPlayer'; -export { useAudioRecorder } from '@/react/useAudioRecorder'; -export { useEdgeSpeech } from '@/react/useEdgeSpeech'; -export { useMicrosoftSpeech } from '@/react/useMicrosoftSpeech'; -export { - type OpenaiSpeechRecognitionOptions, - type OpenaiSTTFetcher, - useOpenaiSTT, - useOpenaiSTTWithPSR, - useOpenaiSTTWithRecord, - useOpenaiSTTWithSR, -} from '@/react/useOpenaiSTT'; -export { useOpenaiTTS } from '@/react/useOpenaiTTS'; -export { usePersistedSpeechRecognition } from '@/react/useSpeechRecognition/usePersistedSpeechRecognition'; -export { - type SpeechRecognitionOptions, - useSpeechRecognition, -} from '@/react/useSpeechRecognition/useSpeechRecognition'; -export { useSpeechSynthes } from '@/react/useSpeechSynthes'; diff --git a/src/react/index.ts b/src/react/index.ts new file mode 100644 index 0000000..8b49138 --- /dev/null +++ b/src/react/index.ts @@ -0,0 +1,24 @@ +export { default as AudioPlayer, type AudioPlayerProps } from './AudioPlayer'; +export { default as AudioVisualizer, type AudioVisualizerProps } from './AudioVisualizer'; +export { useAudioPlayer } from './hooks/useAudioPlayer'; +export { useAudioVisualizer } from './hooks/useAudioVisualizer'; +export { useBlobUrl } from './hooks/useBlobUrl'; +export { useStreamAudioPlayer } from './hooks/useStreamAudioPlayer'; +export { useAudioRecorder } from './useAudioRecorder'; +export { useEdgeSpeech } from './useEdgeSpeech'; +export { useMicrosoftSpeech } from './useMicrosoftSpeech'; +export { + type OpenaiSpeechRecognitionOptions, + type OpenaiSTTFetcher, + useOpenaiSTT, + useOpenaiSTTWithPSR, + useOpenaiSTTWithRecord, + useOpenaiSTTWithSR, +} from './useOpenaiSTT'; +export { useOpenaiTTS } from './useOpenaiTTS'; +export { usePersistedSpeechRecognition } from './useSpeechRecognition/usePersistedSpeechRecognition'; +export { + type SpeechRecognitionOptions, + useSpeechRecognition, +} from './useSpeechRecognition/useSpeechRecognition'; +export { useSpeechSynthes } from './useSpeechSynthes'; From 1ec2701669ba680c426e29fa294e1f85c13a4a76 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 10:04:22 +0000 Subject: [PATCH 06/24] :bookmark: chore(release): v1.12.1-beta.3 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.3](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.2...v1.12.1-beta.3) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: Fix react entry.
Improvements and Fixes #### Code refactoring * **misc**: Fix react entry ([6a4631d](https://github.com/lobehub/lobe-tts/commit/6a4631d))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac47f80..9c3f336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.3](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.2...v1.12.1-beta.3) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: Fix react entry. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Fix react entry ([6a4631d](https://github.com/lobehub/lobe-tts/commit/6a4631d)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.1...v1.12.1-beta.2) Released on **2023-11-15** diff --git a/package.json b/package.json index 9401975..fbfa5bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.2", + "version": "1.12.1-beta.3", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 926260802d384f1db6d90a62b8ace0269df0304c Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 15 Nov 2023 20:51:24 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Fix=20client=20fetc?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/open-stt.ts | 6 ++- src/const/api.ts | 6 +++ ...eateOpenaiAudioTranscriptionsCompletion.ts | 2 +- src/services/fetchEdgeSpeech.ts | 4 ++ src/services/fetchOpenaiSTT.ts | 39 +++++++++++++------ src/services/fetchOpenaiTTS.ts | 19 +++++---- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/api/open-stt.ts b/api/open-stt.ts index 32ae42b..1e1387d 100644 --- a/api/open-stt.ts +++ b/api/open-stt.ts @@ -15,5 +15,9 @@ export default async (req: Request) => { if (!OPENAI_API_KEY) return new Response('OPENAI_API_KEY is not set', { status: 500 }); const openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_PROXY_URL }); const res = await createOpenaiAudioTranscriptionsCompletion({ openai, payload }); - return res; + return new Response(JSON.stringify(res), { + headers: { + 'content-type': 'application/json;charset=UTF-8', + }, + }); }; diff --git a/src/const/api.ts b/src/const/api.ts index ca4fbad..4cae9e0 100644 --- a/src/const/api.ts +++ b/src/const/api.ts @@ -1,3 +1,5 @@ +import urlJoin from 'url-join'; + export const MICROSOFT_SPEECH_URL = 'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak'; export const EDGE_SPEECH_URL = @@ -19,3 +21,7 @@ export const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL || process.env.NEXT_PUBLIC_OPENAI_PROXY_URL || 'https://api.openai.com/v1'; + +export const OPENAI_TTS_URL = (api?: string) => urlJoin(api || OPENAI_PROXY_URL, 'audio/speech'); +export const OPENAI_STT_URL = (api?: string) => + urlJoin(api || OPENAI_PROXY_URL, 'audio/transcriptions'); diff --git a/src/server/createOpenaiAudioTranscriptionsCompletion.ts b/src/server/createOpenaiAudioTranscriptionsCompletion.ts index 7c3ec30..880cb3c 100644 --- a/src/server/createOpenaiAudioTranscriptionsCompletion.ts +++ b/src/server/createOpenaiAudioTranscriptionsCompletion.ts @@ -25,5 +25,5 @@ export const createOpenaiAudioTranscriptionsCompletion = async ({ { headers: { Accept: '*/*' } }, ); - return response.text; + return response; }; diff --git a/src/services/fetchEdgeSpeech.ts b/src/services/fetchEdgeSpeech.ts index 9c18349..0575ec4 100644 --- a/src/services/fetchEdgeSpeech.ts +++ b/src/services/fetchEdgeSpeech.ts @@ -19,6 +19,10 @@ export const fetchEdgeSpeech = async ( ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) : await createEdgeSpeechComletion({ payload })); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const arrayBuffer = await response.arrayBuffer(); const audioBuffer = await arrayBufferConvert(arrayBuffer); return audioBuffer; diff --git a/src/services/fetchOpenaiSTT.ts b/src/services/fetchOpenaiSTT.ts index 4f7a6e4..e687db3 100644 --- a/src/services/fetchOpenaiSTT.ts +++ b/src/services/fetchOpenaiSTT.ts @@ -1,10 +1,18 @@ -import OpenAI from 'openai'; - -import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; -import { createOpenaiAudioTranscriptionsCompletion } from '@/server/createOpenaiAudioTranscriptionsCompletion'; +import { OPENAI_API_KEY, OPENAI_PROXY_URL, OPENAI_STT_URL } from '@/const/api'; import { OpenAISTTPayload } from '@/server/types'; import { RecordMineType, getRecordMineType } from '@/utils/getRecordMineType'; +const genSTTBody = ({ blob, options }: OpenAISTTPayload) => { + const filename = `${Date.now()}.${options.mineType.extension}`; + const file = new File([blob], filename, { + type: options.mineType.mineType, + }); + + const body = new FormData(); + body.append('file', file); + body.append('model', options.model); + return body; +}; export interface OpenaiSttOptions { api?: { key?: string; @@ -14,8 +22,6 @@ export interface OpenaiSttOptions { mineType?: RecordMineType; model?: 'whisper-1'; } - -// 纯文本生成语音 export const fetchOpenaiSTT = async ( speech: Blob, { api = {}, model = 'whisper-1', mineType }: OpenaiSttOptions, @@ -31,12 +37,21 @@ export const fetchOpenaiSTT = async ( }, }; - const response = (await (api?.url + const response = await (api?.url ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) - : createOpenaiAudioTranscriptionsCompletion({ - openai: new OpenAI({ apiKey: key, baseURL: url }), - payload, - }))) as string; + : fetch(OPENAI_STT_URL(url), { + body: genSTTBody(payload), + headers: new Headers({ + Authorization: `Bearer ${key}`, + }), + method: 'POST', + })); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const json = await response.json(); - return response; + return json.text; }; diff --git a/src/services/fetchOpenaiTTS.ts b/src/services/fetchOpenaiTTS.ts index 9007563..281b0a2 100644 --- a/src/services/fetchOpenaiTTS.ts +++ b/src/services/fetchOpenaiTTS.ts @@ -1,7 +1,4 @@ -import OpenAI from 'openai'; - -import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; -import { createOpenaiAudioSpeechCompletion } from '@/server/createOpenaiAudioSpeechCompletion'; +import { OPENAI_API_KEY, OPENAI_PROXY_URL, OPENAI_TTS_URL } from '@/const/api'; import { OpenAITTSPayload } from '@/server/types'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; import { type SsmlOptions } from '@/utils/genSSML'; @@ -34,9 +31,17 @@ export const fetchOpenaiTTS = async ( const response = await (api?.url ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) - : await createOpenaiAudioSpeechCompletion({ - openai: new OpenAI({ apiKey: key, baseURL: url }), - payload, + : fetch(OPENAI_TTS_URL(url), { + body: JSON.stringify({ + input, + model, + voice, + }), + headers: new Headers({ + 'Authorization': `Bearer ${key}`, + 'Content-Type': 'application/json', + }), + method: 'POST', })); if (!response.ok) { From e7296c489334b6dbb14fe8bfce5a707cd6a525cf Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 12:52:55 +0000 Subject: [PATCH 08/24] :bookmark: chore(release): v1.12.1-beta.4 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.4](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.3...v1.12.1-beta.4) Released on **2023-11-15** #### 🐛 Bug Fixes - **misc**: Fix client fetch.
Improvements and Fixes #### What's fixed * **misc**: Fix client fetch ([9262608](https://github.com/lobehub/lobe-tts/commit/9262608))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c3f336..0a1bdc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.4](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.3...v1.12.1-beta.4) + +Released on **2023-11-15** + +#### 🐛 Bug Fixes + +- **misc**: Fix client fetch. + +
+ +
+Improvements and Fixes + +#### What's fixed + +- **misc**: Fix client fetch ([9262608](https://github.com/lobehub/lobe-tts/commit/9262608)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.3](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.2...v1.12.1-beta.3) Released on **2023-11-15** diff --git a/package.json b/package.json index fbfa5bb..349c7c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.3", + "version": "1.12.1-beta.4", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From e69bb5f3528e55fc48010ab566a941a65159067b Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 21:12:02 +0800 Subject: [PATCH 09/24] :wrench: chore: fix build --- .dumirc.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.dumirc.ts b/.dumirc.ts index 3785f0f..1b51b39 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -1,4 +1,5 @@ import { defineConfig } from 'dumi'; +import path from 'node:path'; import { description, homepage, name } from './package.json'; @@ -41,6 +42,9 @@ const themeConfig = { }; export default defineConfig({ + alias: { + '@lobehub/tts/react': path.join(__dirname, './src/react'), + }, apiParser: isProduction ? {} : false, base: '/', define: { @@ -53,7 +57,7 @@ export default defineConfig({ publicPath: '/', resolve: { atomDirs: [{ dir: 'src/react', type: 'component' }], - entryFile: isProduction ? './src/index.ts' : undefined, + entryFile: isProduction ? './src/react/index.ts' : undefined, }, styles: [ `html, body { background: transparent; } From 7338a9ef891f5eef2e5302549886614c83c1e436 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 21:25:02 +0800 Subject: [PATCH 10/24] :wrench: chore: fix build --- .dumirc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.dumirc.ts b/.dumirc.ts index 1b51b39..d957106 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -57,7 +57,7 @@ export default defineConfig({ publicPath: '/', resolve: { atomDirs: [{ dir: 'src/react', type: 'component' }], - entryFile: isProduction ? './src/react/index.ts' : undefined, + entryFile: isProduction ? './src/index.ts' : undefined, }, styles: [ `html, body { background: transparent; } From d875be6b33756b0eca8a65e1fde2e4c6832d802b Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 21:39:51 +0800 Subject: [PATCH 11/24] :wrench: refactor: refactor to fix build --- api/edge-speech.ts | 8 ++++---- api/microsoft-speech.ts | 3 +-- api/open-stt.ts | 15 +++++++++------ api/openai-tts.ts | 16 ++++++++-------- src/const/api.ts | 12 +++--------- src/services/fetchOpenaiSTT.ts | 5 ++--- src/services/fetchOpenaiTTS.ts | 5 ++--- 7 files changed, 29 insertions(+), 35 deletions(-) diff --git a/api/edge-speech.ts b/api/edge-speech.ts index c4f9639..e3e558b 100644 --- a/api/edge-speech.ts +++ b/api/edge-speech.ts @@ -1,5 +1,4 @@ -import { createEdgeSpeechComletion } from '../src/server/createEdgeSpeechComletion'; -import { EdgeSpeechPayload } from '../src/server/types'; +import { EdgeSpeechPayload, createEdgeSpeechComletion } from '@lobehub/tts'; export const config = { runtime: 'edge', @@ -7,7 +6,8 @@ export const config = { export default async (req: Request) => { if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); + const payload = (await req.json()) as EdgeSpeechPayload; - const res = await createEdgeSpeechComletion({ payload }); - return res; + + return createEdgeSpeechComletion({ payload }); }; diff --git a/api/microsoft-speech.ts b/api/microsoft-speech.ts index 38e3f02..ae4c380 100644 --- a/api/microsoft-speech.ts +++ b/api/microsoft-speech.ts @@ -1,5 +1,4 @@ -import { createMicrosoftSpeechComletion } from '../src/server/createMicrosoftSpeechComletion'; -import { MicrosoftSpeechPayload } from '../src/server/types'; +import { MicrosoftSpeechPayload, createMicrosoftSpeechComletion } from '@lobehub/tts'; export const config = { runtime: 'edge', diff --git a/api/open-stt.ts b/api/open-stt.ts index 1e1387d..abfd891 100644 --- a/api/open-stt.ts +++ b/api/open-stt.ts @@ -1,20 +1,23 @@ +import { OpenAISTTPayload, createOpenaiAudioTranscriptionsCompletion } from '@lobehub/tts'; import OpenAI from 'openai'; -import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; - -import { createOpenaiAudioTranscriptionsCompletion } from '../src/server/createOpenaiAudioTranscriptionsCompletion'; -import { OpenAISTTPayload } from '../src/server/types'; - export const config = { runtime: 'edge', }; export default async (req: Request) => { if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); - const payload = (await req.json()) as OpenAISTTPayload; + + const OPENAI_API_KEY = process.env.OPENAI_API_KEY; + const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL; + if (!OPENAI_API_KEY) return new Response('OPENAI_API_KEY is not set', { status: 500 }); + + const payload = (await req.json()) as OpenAISTTPayload; + const openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_PROXY_URL }); const res = await createOpenaiAudioTranscriptionsCompletion({ openai, payload }); + return new Response(JSON.stringify(res), { headers: { 'content-type': 'application/json;charset=UTF-8', diff --git a/api/openai-tts.ts b/api/openai-tts.ts index 88ecbfe..fa95fa0 100644 --- a/api/openai-tts.ts +++ b/api/openai-tts.ts @@ -1,19 +1,19 @@ +import { OpenAITTSPayload, createOpenaiAudioSpeechCompletion } from '@lobehub/tts'; import OpenAI from 'openai'; -import { OPENAI_API_KEY, OPENAI_PROXY_URL } from '@/const/api'; - -import { createOpenaiAudioSpeechCompletion } from '../src/server/createOpenaiAudioSpeechCompletion'; -import { OpenAITTSPayload } from '../src/server/types'; - export const config = { runtime: 'edge', }; export default async (req: Request) => { if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); - const payload = (await req.json()) as OpenAITTSPayload; + const OPENAI_API_KEY = process.env.OPENAI_API_KEY; + const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL; + if (!OPENAI_API_KEY) return new Response('OPENAI_API_KEY is not set', { status: 500 }); + const payload = (await req.json()) as OpenAITTSPayload; + const openai = new OpenAI({ apiKey: OPENAI_API_KEY, baseURL: OPENAI_PROXY_URL }); - const res = await createOpenaiAudioSpeechCompletion({ openai, payload }); - return res; + + return createOpenaiAudioSpeechCompletion({ openai, payload }); }; diff --git a/src/const/api.ts b/src/const/api.ts index 4cae9e0..2b5efd8 100644 --- a/src/const/api.ts +++ b/src/const/api.ts @@ -5,6 +5,7 @@ export const MICROSOFT_SPEECH_URL = export const EDGE_SPEECH_URL = 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1'; export const EDGE_API_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; +export const OPENAI_BASE_URL = 'https://api.openai.com/v1'; export const MICROSOFT_SPEECH_API_URL = '/api/microsoft-speech'; export const EDGE_SPEECH_API_URL = '/api/edge-speech'; @@ -15,13 +16,6 @@ export const AZURE_SPEECH_KEY = process.env.AZURE_SPEECH_KEY || process.env.NEXT_PUBLIC_AZURE_SPEECH_KEY || ''; export const AZURE_SPEECH_REGION = process.env.AZURE_SPEECH_REGION || process.env.NEXT_PUBLIC_AZURE_SPEECH_REGION || ''; -export const OPENAI_API_KEY = - process.env.OPENAI_API_KEY || process.env.NEXT_PUBLIC_OPENAI_API_KEY || ''; -export const OPENAI_PROXY_URL = - process.env.OPENAI_PROXY_URL || - process.env.NEXT_PUBLIC_OPENAI_PROXY_URL || - 'https://api.openai.com/v1'; -export const OPENAI_TTS_URL = (api?: string) => urlJoin(api || OPENAI_PROXY_URL, 'audio/speech'); -export const OPENAI_STT_URL = (api?: string) => - urlJoin(api || OPENAI_PROXY_URL, 'audio/transcriptions'); +export const OPENAI_TTS_URL = (api: string) => urlJoin(api, 'audio/speech'); +export const OPENAI_STT_URL = (api: string) => urlJoin(api, 'audio/transcriptions'); diff --git a/src/services/fetchOpenaiSTT.ts b/src/services/fetchOpenaiSTT.ts index e687db3..4fa8677 100644 --- a/src/services/fetchOpenaiSTT.ts +++ b/src/services/fetchOpenaiSTT.ts @@ -1,4 +1,4 @@ -import { OPENAI_API_KEY, OPENAI_PROXY_URL, OPENAI_STT_URL } from '@/const/api'; +import { OPENAI_BASE_URL, OPENAI_STT_URL } from '@/const/api'; import { OpenAISTTPayload } from '@/server/types'; import { RecordMineType, getRecordMineType } from '@/utils/getRecordMineType'; @@ -26,8 +26,7 @@ export const fetchOpenaiSTT = async ( speech: Blob, { api = {}, model = 'whisper-1', mineType }: OpenaiSttOptions, ): Promise => { - const key = api?.key || OPENAI_API_KEY; - const url = api?.proxy || OPENAI_PROXY_URL; + const { key, url = OPENAI_BASE_URL } = api; const payload: OpenAISTTPayload = { blob: speech, diff --git a/src/services/fetchOpenaiTTS.ts b/src/services/fetchOpenaiTTS.ts index 281b0a2..45d45a9 100644 --- a/src/services/fetchOpenaiTTS.ts +++ b/src/services/fetchOpenaiTTS.ts @@ -1,4 +1,4 @@ -import { OPENAI_API_KEY, OPENAI_PROXY_URL, OPENAI_TTS_URL } from '@/const/api'; +import { OPENAI_BASE_URL, OPENAI_TTS_URL } from '@/const/api'; import { OpenAITTSPayload } from '@/server/types'; import { arrayBufferConvert } from '@/utils/arrayBufferConvert'; import { type SsmlOptions } from '@/utils/genSSML'; @@ -18,8 +18,7 @@ export const fetchOpenaiTTS = async ( input: string, { api = {}, model = 'tts-1', voice }: OpenaiTtsOptions, ): Promise => { - const key = api?.key || OPENAI_API_KEY; - const url = api?.proxy || OPENAI_PROXY_URL; + const { key, url = OPENAI_BASE_URL } = api; const payload: OpenAITTSPayload = { input, From 761f22b1fb883d59d0ae0ade38369558a51c9464 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 13:40:46 +0000 Subject: [PATCH 12/24] :bookmark: chore(release): v1.12.1-beta.5 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.5](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.4...v1.12.1-beta.5) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: Refactor to fix build.
Improvements and Fixes #### Code refactoring * **misc**: Refactor to fix build ([d875be6](https://github.com/lobehub/lobe-tts/commit/d875be6))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a1bdc3..e6dae22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.5](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.4...v1.12.1-beta.5) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: Refactor to fix build. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Refactor to fix build ([d875be6](https://github.com/lobehub/lobe-tts/commit/d875be6)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.4](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.3...v1.12.1-beta.4) Released on **2023-11-15** diff --git a/package.json b/package.json index 349c7c3..205ec96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.4", + "version": "1.12.1-beta.5", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 9858fcb52bfdfed03aca7fbcbd91ea5fdc3d2cee Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 21:47:39 +0800 Subject: [PATCH 13/24] :wrench: refactor: refactor to fix build --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 205ec96..eff35e5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "build:server": "tsc server.ts --declaration", "ci": "npm run lint && npm run type-check", "dev": "father dev", - "docs:build": "npm run setup && dumi build", + "docs:build": "npm run setup && npm run build && dumi build", "docs:build-analyze": "ANALYZE=1 dumi build", "docs:dev": "npm run setup && dumi dev", "doctor": "father doctor", From 4c22bea22513df7e457cd21252b26917dae258de Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 13:48:44 +0000 Subject: [PATCH 14/24] :bookmark: chore(release): v1.12.1-beta.6 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### [Version 1.12.1-beta.6](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.5...v1.12.1-beta.6) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: Refactor to fix build.
Improvements and Fixes #### Code refactoring * **misc**: Refactor to fix build ([9858fcb](https://github.com/lobehub/lobe-tts/commit/9858fcb))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6dae22..bd8d431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +### [Version 1.12.1-beta.6](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.5...v1.12.1-beta.6) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: Refactor to fix build. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Refactor to fix build ([9858fcb](https://github.com/lobehub/lobe-tts/commit/9858fcb)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.5](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.4...v1.12.1-beta.5) Released on **2023-11-15** diff --git a/package.json b/package.json index eff35e5..7756a0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.5", + "version": "1.12.1-beta.6", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 9c41986a76e2fb6f2023012b33eccbf69e91cdb7 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 21:51:47 +0800 Subject: [PATCH 15/24] :wrench: ci: use pure esm to fix build --- .changelogrc.js => .changelogrc.cjs | 0 .commitlintrc.js => .commitlintrc.cjs | 0 .eslintrc.js => .eslintrc.cjs | 0 .prettierrc.js => .prettierrc.cjs | 0 .releaserc.js => .releaserc.cjs | 0 .remarkrc.js => .remarkrc.cjs | 0 package.json | 1 + 7 files changed, 1 insertion(+) rename .changelogrc.js => .changelogrc.cjs (100%) rename .commitlintrc.js => .commitlintrc.cjs (100%) rename .eslintrc.js => .eslintrc.cjs (100%) rename .prettierrc.js => .prettierrc.cjs (100%) rename .releaserc.js => .releaserc.cjs (100%) rename .remarkrc.js => .remarkrc.cjs (100%) diff --git a/.changelogrc.js b/.changelogrc.cjs similarity index 100% rename from .changelogrc.js rename to .changelogrc.cjs diff --git a/.commitlintrc.js b/.commitlintrc.cjs similarity index 100% rename from .commitlintrc.js rename to .commitlintrc.cjs diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 100% rename from .eslintrc.js rename to .eslintrc.cjs diff --git a/.prettierrc.js b/.prettierrc.cjs similarity index 100% rename from .prettierrc.js rename to .prettierrc.cjs diff --git a/.releaserc.js b/.releaserc.cjs similarity index 100% rename from .releaserc.js rename to .releaserc.cjs diff --git a/.remarkrc.js b/.remarkrc.cjs similarity index 100% rename from .remarkrc.js rename to .remarkrc.cjs diff --git a/package.json b/package.json index 7756a0b..f0b7519 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "MIT", "author": "LobeHub ", "sideEffects": false, + "type": "module", "exports": { "./package.json": "./package.json", ".": { From 07245e362dadd6ed2ed7dd77858c39fba2804b64 Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 15 Nov 2023 21:58:33 +0800 Subject: [PATCH 16/24] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - src/class/EdgeSpeechTTS.ts | 20 +++++++++++++++++ src/class/MicorsoftSpeechTTS.ts | 20 +++++++++++++++++ src/class/OpenaiSTT.ts | 5 +++++ src/class/OpenaiTTS.ts | 14 ++++++++++++ src/class/VoiceList.ts | 38 +++++++++++++++++++++++++++++++++ src/index.ts | 5 +++++ src/services/fetchEdgeSpeech.ts | 2 +- 8 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/class/EdgeSpeechTTS.ts create mode 100644 src/class/MicorsoftSpeechTTS.ts create mode 100644 src/class/OpenaiSTT.ts create mode 100644 src/class/OpenaiTTS.ts create mode 100644 src/class/VoiceList.ts diff --git a/package.json b/package.json index f0b7519..ceb84bb 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ ], "scripts": { "build": "father build", - "build:server": "tsc server.ts --declaration", "ci": "npm run lint && npm run type-check", "dev": "father dev", "docs:build": "npm run setup && npm run build && dumi build", diff --git a/src/class/EdgeSpeechTTS.ts b/src/class/EdgeSpeechTTS.ts new file mode 100644 index 0000000..895bcac --- /dev/null +++ b/src/class/EdgeSpeechTTS.ts @@ -0,0 +1,20 @@ +import edgeVoiceList from '@/data/edgeVoiceList'; +import voiceName from '@/data/voiceList'; +import { fetchEdgeSpeech } from '@/services/fetchEdgeSpeech'; +import { getEdgeVoiceOptions, getVoiceLocaleOptions } from '@/utils/getVoiceList'; + +export class EdgeSpeechTTS { + private locale?: string; + constructor(locale?: string) { + this.locale = locale; + } + get voiceOptions() { + return getEdgeVoiceOptions(this.locale); + } + get localeOptions() { + return getVoiceLocaleOptions(); + } + voiceList = edgeVoiceList; + voiceName = voiceName; + fetch = fetchEdgeSpeech; +} diff --git a/src/class/MicorsoftSpeechTTS.ts b/src/class/MicorsoftSpeechTTS.ts new file mode 100644 index 0000000..85ca3ae --- /dev/null +++ b/src/class/MicorsoftSpeechTTS.ts @@ -0,0 +1,20 @@ +import azureVoiceList from '@/data/azureVoiceList'; +import voiceName from '@/data/voiceList'; +import { fetchMicrosoftSpeech } from '@/services/fetchMicrosoftSpeech'; +import { getAzureVoiceOptions, getVoiceLocaleOptions } from '@/utils/getVoiceList'; + +export class MicorsoftSpeechTTS { + private locale?: string; + constructor(locale?: string) { + this.locale = locale; + } + get voiceOptions() { + return getAzureVoiceOptions(this.locale); + } + get localeOptions() { + return getVoiceLocaleOptions(); + } + voiceList = azureVoiceList; + voiceName = voiceName; + fetch = fetchMicrosoftSpeech; +} diff --git a/src/class/OpenaiSTT.ts b/src/class/OpenaiSTT.ts new file mode 100644 index 0000000..eafd676 --- /dev/null +++ b/src/class/OpenaiSTT.ts @@ -0,0 +1,5 @@ +import { fetchOpenaiSTT } from '@/services/fetchOpenaiSTT'; + +export class OpenaiSTT { + fetch = fetchOpenaiSTT; +} diff --git a/src/class/OpenaiTTS.ts b/src/class/OpenaiTTS.ts new file mode 100644 index 0000000..cf7b555 --- /dev/null +++ b/src/class/OpenaiTTS.ts @@ -0,0 +1,14 @@ +import openaiVoiceList from '@/data/openaiVoiceList'; +import { fetchOpenaiTTS } from '@/services/fetchOpenaiTTS'; +import { getOpenaiVoiceOptions, getVoiceLocaleOptions } from '@/utils/getVoiceList'; + +export class OpenaiTTS { + get voiceOptions() { + return getOpenaiVoiceOptions(); + } + get localeOptions() { + return getVoiceLocaleOptions(); + } + voiceList = openaiVoiceList; + fetch = fetchOpenaiTTS; +} diff --git a/src/class/VoiceList.ts b/src/class/VoiceList.ts new file mode 100644 index 0000000..18c6c5c --- /dev/null +++ b/src/class/VoiceList.ts @@ -0,0 +1,38 @@ +import { + getAzureVoiceOptions, + getEdgeVoiceOptions, + getOpenaiVoiceOptions, + getSpeechSynthesVoiceOptions, + getVoiceLocaleOptions, +} from '@/utils/getVoiceList'; + +export class VoiceList { + private locale?: string; + constructor(locale?: string) { + this.locale = locale; + } + + get speechSynthesVoiceOptions() { + return getSpeechSynthesVoiceOptions(this.locale); + } + + get azureVoiceOptions() { + return getAzureVoiceOptions(this.locale); + } + + get edgeVoiceOptions() { + return getEdgeVoiceOptions(this.locale); + } + + get microsoftVoiceOptions() { + return getEdgeVoiceOptions(this.locale); + } + + get openaiVoiceOptions() { + return getOpenaiVoiceOptions(); + } + + get localeOptions() { + return getVoiceLocaleOptions(); + } +} diff --git a/src/index.ts b/src/index.ts index c819e1f..4f206be 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,8 @@ +export { EdgeSpeechTTS } from './class/EdgeSpeechTTS'; +export { MicorsoftSpeechTTS } from './class/MicorsoftSpeechTTS'; +export { OpenaiSTT } from './class/OpenaiSTT'; +export { OpenaiTTS } from './class/OpenaiTTS'; +export { VoiceList } from './class/VoiceList'; export { default as azureVoiceList } from './data/azureVoiceList'; export { default as edgeVoiceList } from './data/edgeVoiceList'; export { default as voiceLocale } from './data/locales'; diff --git a/src/services/fetchEdgeSpeech.ts b/src/services/fetchEdgeSpeech.ts index 0575ec4..c85cd0c 100644 --- a/src/services/fetchEdgeSpeech.ts +++ b/src/services/fetchEdgeSpeech.ts @@ -17,7 +17,7 @@ export const fetchEdgeSpeech = async ( const response = await (api?.url ? fetch(api.url, { body: JSON.stringify(payload), method: 'POST' }) - : await createEdgeSpeechComletion({ payload })); + : createEdgeSpeechComletion({ payload })); if (!response.ok) { throw new Error('Network response was not ok'); From 1593a5a81b7a76ba799f69d4b71b1739b32f9d4c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 14:00:48 +0000 Subject: [PATCH 17/24] :bookmark: chore(release): v1.13.0-beta.1 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [Version 1.13.0-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.6...v1.13.0-beta.1) Released on **2023-11-15** #### ✨ Features - **misc**: Add class.
Improvements and Fixes #### What's improved * **misc**: Add class ([07245e3](https://github.com/lobehub/lobe-tts/commit/07245e3))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd8d431..61592cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +## [Version 1.13.0-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.6...v1.13.0-beta.1) + +Released on **2023-11-15** + +#### ✨ Features + +- **misc**: Add class. + +
+ +
+Improvements and Fixes + +#### What's improved + +- **misc**: Add class ([07245e3](https://github.com/lobehub/lobe-tts/commit/07245e3)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ### [Version 1.12.1-beta.6](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.5...v1.12.1-beta.6) Released on **2023-11-15** diff --git a/package.json b/package.json index ceb84bb..e8a82f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.12.1-beta.6", + "version": "1.13.0-beta.1", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From b5592070ba2a6e48d8649fa3874e5f5de7b8a88c Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 22:02:23 +0800 Subject: [PATCH 18/24] :wrench: chore: try to fix --- api/microsoft-speech.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/microsoft-speech.ts b/api/microsoft-speech.ts index ae4c380..066bf59 100644 --- a/api/microsoft-speech.ts +++ b/api/microsoft-speech.ts @@ -1,4 +1,4 @@ -import { MicrosoftSpeechPayload, createMicrosoftSpeechComletion } from '@lobehub/tts'; +import { MicrosoftSpeechPayload, createMicrosoftSpeechComletion } from '@/index'; export const config = { runtime: 'edge', @@ -7,6 +7,6 @@ export const config = { export default async (req: Request) => { if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); const payload = (await req.json()) as MicrosoftSpeechPayload; - const res = await createMicrosoftSpeechComletion({ payload }); - return res; + + return createMicrosoftSpeechComletion({ payload }); }; From 6be487f31cd29d3e61efb5a798a0f52162f63591 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 22:05:53 +0800 Subject: [PATCH 19/24] :wrench: chore: try to fix --- api/microsoft-speech.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/microsoft-speech.ts b/api/microsoft-speech.ts index 066bf59..48e2408 100644 --- a/api/microsoft-speech.ts +++ b/api/microsoft-speech.ts @@ -1,4 +1,5 @@ -import { MicrosoftSpeechPayload, createMicrosoftSpeechComletion } from '@/index'; +import { createMicrosoftSpeechComletion } from '../src/server/createMicrosoftSpeechComletion'; +import { MicrosoftSpeechPayload } from '../src/server/types'; export const config = { runtime: 'edge', From 7da2821b07217efc6a9d9f0401719d0013c48c5e Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 22:09:37 +0800 Subject: [PATCH 20/24] :wrench: chore: try to fix --- api/edge-speech.ts | 3 ++- api/open-stt.ts | 4 +++- api/openai-tts.ts | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/edge-speech.ts b/api/edge-speech.ts index e3e558b..9bee67d 100644 --- a/api/edge-speech.ts +++ b/api/edge-speech.ts @@ -1,4 +1,5 @@ -import { EdgeSpeechPayload, createEdgeSpeechComletion } from '@lobehub/tts'; +import { createEdgeSpeechComletion } from '../src/server/createEdgeSpeechComletion'; +import { EdgeSpeechPayload } from '../src/server/types'; export const config = { runtime: 'edge', diff --git a/api/open-stt.ts b/api/open-stt.ts index abfd891..9576821 100644 --- a/api/open-stt.ts +++ b/api/open-stt.ts @@ -1,6 +1,8 @@ -import { OpenAISTTPayload, createOpenaiAudioTranscriptionsCompletion } from '@lobehub/tts'; import OpenAI from 'openai'; +import { createOpenaiAudioTranscriptionsCompletion } from '../src/server/createOpenaiAudioTranscriptionsCompletion'; +import { OpenAISTTPayload } from '../src/server/types'; + export const config = { runtime: 'edge', }; diff --git a/api/openai-tts.ts b/api/openai-tts.ts index fa95fa0..2338729 100644 --- a/api/openai-tts.ts +++ b/api/openai-tts.ts @@ -1,6 +1,8 @@ -import { OpenAITTSPayload, createOpenaiAudioSpeechCompletion } from '@lobehub/tts'; import OpenAI from 'openai'; +import { createOpenaiAudioSpeechCompletion } from '../src/server/createOpenaiAudioSpeechCompletion'; +import { OpenAITTSPayload } from '../src/server/types'; + export const config = { runtime: 'edge', }; From 2c49e024e5119209829b67b817566998a077358c Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 15 Nov 2023 22:50:02 +0800 Subject: [PATCH 21/24] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20SWR=20config=20?= =?UTF-8?q?to=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...softSpeechTTS.ts => MicrosoftSpeechTTS.ts} | 2 +- src/index.ts | 2 +- src/react/index.ts | 1 - src/react/useEdgeSpeech/index.ts | 15 ++++-- src/react/useMicrosoftSpeech/index.ts | 15 ++++-- src/react/useOpenaiSTT/index.ts | 2 +- src/react/useOpenaiSTT/useOpenaiSTT.ts | 6 +-- src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts | 47 ++++++++++------- .../useOpenaiSTT/useOpenaiSTTWithRecord.ts | 52 ++++++++++++------- src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts | 47 ++++++++++------- src/react/useOpenaiTTS/index.ts | 15 ++++-- src/react/useTTS/index.ts | 21 ++++++-- 12 files changed, 141 insertions(+), 84 deletions(-) rename src/class/{MicorsoftSpeechTTS.ts => MicrosoftSpeechTTS.ts} (94%) diff --git a/src/class/MicorsoftSpeechTTS.ts b/src/class/MicrosoftSpeechTTS.ts similarity index 94% rename from src/class/MicorsoftSpeechTTS.ts rename to src/class/MicrosoftSpeechTTS.ts index 85ca3ae..2362078 100644 --- a/src/class/MicorsoftSpeechTTS.ts +++ b/src/class/MicrosoftSpeechTTS.ts @@ -3,7 +3,7 @@ import voiceName from '@/data/voiceList'; import { fetchMicrosoftSpeech } from '@/services/fetchMicrosoftSpeech'; import { getAzureVoiceOptions, getVoiceLocaleOptions } from '@/utils/getVoiceList'; -export class MicorsoftSpeechTTS { +export class MicrosoftSpeechTTS { private locale?: string; constructor(locale?: string) { this.locale = locale; diff --git a/src/index.ts b/src/index.ts index 4f206be..c026470 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ export { EdgeSpeechTTS } from './class/EdgeSpeechTTS'; -export { MicorsoftSpeechTTS } from './class/MicorsoftSpeechTTS'; +export { MicrosoftSpeechTTS } from './class/MicrosoftSpeechTTS'; export { OpenaiSTT } from './class/OpenaiSTT'; export { OpenaiTTS } from './class/OpenaiTTS'; export { VoiceList } from './class/VoiceList'; diff --git a/src/react/index.ts b/src/react/index.ts index 8b49138..f5bc749 100644 --- a/src/react/index.ts +++ b/src/react/index.ts @@ -9,7 +9,6 @@ export { useEdgeSpeech } from './useEdgeSpeech'; export { useMicrosoftSpeech } from './useMicrosoftSpeech'; export { type OpenaiSpeechRecognitionOptions, - type OpenaiSTTFetcher, useOpenaiSTT, useOpenaiSTTWithPSR, useOpenaiSTTWithRecord, diff --git a/src/react/useEdgeSpeech/index.ts b/src/react/useEdgeSpeech/index.ts index bc76f57..e717728 100644 --- a/src/react/useEdgeSpeech/index.ts +++ b/src/react/useEdgeSpeech/index.ts @@ -1,12 +1,19 @@ import { useState } from 'react'; -import { useTTS } from '@/react/useTTS'; +import { TTSConfig, useTTS } from '@/react/useTTS'; import { EdgeSpeechOptions, fetchEdgeSpeech } from '@/services/fetchEdgeSpeech'; -export const useEdgeSpeech = (defaultText: string, options: EdgeSpeechOptions) => { +export const useEdgeSpeech = ( + defaultText: string, + options: EdgeSpeechOptions, + config?: TTSConfig, +) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.voice, text, (segmentText: string) => - fetchEdgeSpeech(segmentText, options), + const rest = useTTS( + options.voice, + text, + (segmentText: string) => fetchEdgeSpeech(segmentText, options), + config, ); return { setText, diff --git a/src/react/useMicrosoftSpeech/index.ts b/src/react/useMicrosoftSpeech/index.ts index 691fcfd..bd17726 100644 --- a/src/react/useMicrosoftSpeech/index.ts +++ b/src/react/useMicrosoftSpeech/index.ts @@ -1,12 +1,19 @@ import { useState } from 'react'; -import { useTTS } from '@/react/useTTS'; +import { TTSConfig, useTTS } from '@/react/useTTS'; import { type MicrosoftSpeechOptions, fetchMicrosoftSpeech } from '@/services/fetchMicrosoftSpeech'; -export const useMicrosoftSpeech = (defaultText: string, options: MicrosoftSpeechOptions) => { +export const useMicrosoftSpeech = ( + defaultText: string, + options: MicrosoftSpeechOptions, + config?: TTSConfig, +) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.voice, text, (segmentText: string) => - fetchMicrosoftSpeech(segmentText, options), + const rest = useTTS( + options.voice, + text, + (segmentText: string) => fetchMicrosoftSpeech(segmentText, options), + config, ); return { setText, diff --git a/src/react/useOpenaiSTT/index.ts b/src/react/useOpenaiSTT/index.ts index 8d5af8b..728150a 100644 --- a/src/react/useOpenaiSTT/index.ts +++ b/src/react/useOpenaiSTT/index.ts @@ -1,4 +1,4 @@ -export { type OpenaiSTTFetcher, useOpenaiSTT } from './useOpenaiSTT'; +export { useOpenaiSTT } from './useOpenaiSTT'; export { useOpenaiSTTWithPSR } from './useOpenaiSTTWithPSR'; export { type OpenaiSpeechRecognitionOptions, diff --git a/src/react/useOpenaiSTT/useOpenaiSTT.ts b/src/react/useOpenaiSTT/useOpenaiSTT.ts index 1f99fac..7080b3b 100644 --- a/src/react/useOpenaiSTT/useOpenaiSTT.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTT.ts @@ -3,23 +3,19 @@ import useSWR, { type SWRConfiguration } from 'swr'; import { OpenaiSttOptions, fetchOpenaiSTT } from '@/services/fetchOpenaiSTT'; import { getRecordMineType } from '@/utils/getRecordMineType'; -export type OpenaiSTTFetcher = (blob: Blob, sttOptions: OpenaiSttOptions) => Promise; export const useOpenaiSTT = ( shouldFetch?: boolean, blob?: Blob, options?: OpenaiSttOptions, config?: SWRConfiguration, - fetcher?: OpenaiSTTFetcher, ) => { const key = new Date().getDate().toString(); const optionsWithMineType: OpenaiSttOptions = { ...options, mineType: getRecordMineType() }; - const openaiSTTFetcher = fetcher ?? fetchOpenaiSTT; - return useSWR( shouldFetch && blob ? key : null, - async () => await openaiSTTFetcher(blob as Blob, optionsWithMineType), + async () => await fetchOpenaiSTT(blob as Blob, optionsWithMineType), config, ); }; diff --git a/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts index 5dc31bd..dfff989 100644 --- a/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithPSR.ts @@ -1,14 +1,22 @@ import { useCallback, useState } from 'react'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; import { usePersistedSpeechRecognition } from '@/react/useSpeechRecognition'; -import { OpenaiSpeechRecognitionOptions } from './useOpenaiSTTWithRecord'; +import { OpenaiSpeechRecognitionOptions, STTConfig } from './useOpenaiSTTWithRecord'; export const useOpenaiSTTWithPSR = ( locale: string, - { onBolbAvailable, onTextChange, ...options }: OpenaiSpeechRecognitionOptions, - fetcher?: OpenaiSTTFetcher, + options: OpenaiSpeechRecognitionOptions, + { + onBolbAvailable, + onTextChange, + onSuccess, + onError, + onFinished, + onStart, + onStop, + }: STTConfig = {}, ) => { const [isGlobalLoading, setIsGlobalLoading] = useState(false); const [shouldFetch, setShouldFetch] = useState(false); @@ -33,34 +41,33 @@ export const useOpenaiSTTWithPSR = ( }); const handleStart = useCallback(() => { + onStart?.(); setIsGlobalLoading(true); start(); setText(''); }, [start]); const handleStop = useCallback(() => { + onStop?.(); stop(); setShouldFetch(false); setIsGlobalLoading(false); }, [stop]); - const { isLoading } = useOpenaiSTT( - shouldFetch, - blob, - options, - { - onError: (err) => { - console.error(err); - handleStop(); - }, - onSuccess: (data) => { - setText(data); - onTextChange?.(data); - handleStop(); - }, + const { isLoading } = useOpenaiSTT(shouldFetch, blob, options, { + onError: (err, ...rest) => { + onError?.(err, ...rest); + console.error(err); + handleStop(); }, - fetcher, - ); + onSuccess: (data, ...rest) => { + onSuccess?.(data, ...rest); + setText(data); + onTextChange?.(data); + handleStop(); + onFinished?.(data, ...rest); + }, + }); return { blob, diff --git a/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts index 820efb5..fd98e02 100644 --- a/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithRecord.ts @@ -1,15 +1,30 @@ import { useCallback, useState } from 'react'; +import { SWRConfiguration } from 'swr'; import { useAudioRecorder } from '@/react/useAudioRecorder'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; import { SpeechRecognitionOptions } from '@/react/useSpeechRecognition/useSpeechRecognition'; import { OpenaiSttOptions } from '@/services/fetchOpenaiSTT'; export type OpenaiSpeechRecognitionOptions = SpeechRecognitionOptions & OpenaiSttOptions; +export interface STTConfig extends SpeechRecognitionOptions, SWRConfiguration { + onFinished?: SWRConfiguration['onSuccess']; + onStart?: () => void; + onStop?: () => void; +} + export const useOpenaiSTTWithRecord = ( - { onBolbAvailable, onTextChange, ...options }: OpenaiSpeechRecognitionOptions, - fetcher?: OpenaiSTTFetcher, + options: OpenaiSttOptions, + { + onBolbAvailable, + onTextChange, + onSuccess, + onError, + onFinished, + onStart, + onStop, + }: STTConfig = {}, ) => { const [isGlobalLoading, setIsGlobalLoading] = useState(false); const [shouldFetch, setShouldFetch] = useState(false); @@ -22,34 +37,33 @@ export const useOpenaiSTTWithRecord = ( ); const handleStart = useCallback(() => { + onStart?.(); setIsGlobalLoading(true); start(); setText(''); }, [start]); const handleStop = useCallback(() => { + onStop?.(); stop(); setShouldFetch(false); setIsGlobalLoading(false); }, [stop]); - const { isLoading } = useOpenaiSTT( - shouldFetch, - blob, - options, - { - onError: (err) => { - console.error(err); - handleStop(); - }, - onSuccess: (data, value) => { - setText(data); - onTextChange?.(value); - handleStop(); - }, + const { isLoading } = useOpenaiSTT(shouldFetch, blob, options, { + onError: (err, ...rest) => { + onError?.(err, ...rest); + console.error(err); + handleStop(); }, - fetcher, - ); + onSuccess: (data, ...rest) => { + onSuccess?.(data, ...rest); + setText(data); + onTextChange?.(data); + handleStop(); + onFinished?.(data, ...rest); + }, + }); return { blob, diff --git a/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts b/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts index 8939c1a..b1bb7f6 100644 --- a/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts +++ b/src/react/useOpenaiSTT/useOpenaiSTTWithSR.ts @@ -1,14 +1,22 @@ import { useCallback, useState } from 'react'; -import { OpenaiSTTFetcher, useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; +import { useOpenaiSTT } from '@/react/useOpenaiSTT/useOpenaiSTT'; import { useSpeechRecognition } from '@/react/useSpeechRecognition'; -import { OpenaiSpeechRecognitionOptions } from './useOpenaiSTTWithRecord'; +import { OpenaiSpeechRecognitionOptions, STTConfig } from './useOpenaiSTTWithRecord'; export const useOpenaiSTTWithSR = ( locale: string, - { onBolbAvailable, onTextChange, ...options }: OpenaiSpeechRecognitionOptions, - fetcher?: OpenaiSTTFetcher, + options: OpenaiSpeechRecognitionOptions, + { + onBolbAvailable, + onTextChange, + onSuccess, + onError, + onFinished, + onStart, + onStop, + }: STTConfig = {}, ) => { const [isGlobalLoading, setIsGlobalLoading] = useState(false); const [shouldFetch, setShouldFetch] = useState(false); @@ -33,34 +41,33 @@ export const useOpenaiSTTWithSR = ( }); const handleStart = useCallback(() => { + onStart?.(); setIsGlobalLoading(true); start(); setText(''); }, [start]); const handleStop = useCallback(() => { + onStop?.(); stop(); setShouldFetch(false); setIsGlobalLoading(false); }, [stop]); - const { isLoading } = useOpenaiSTT( - shouldFetch, - blob, - options, - { - onError: (err) => { - console.error(err); - handleStop(); - }, - onSuccess: (data) => { - setText(data); - onTextChange?.(data); - handleStop(); - }, + const { isLoading } = useOpenaiSTT(shouldFetch, blob, options, { + onError: (err, ...rest) => { + onError?.(err, ...rest); + console.error(err); + handleStop(); }, - fetcher, - ); + onSuccess: (data, ...rest) => { + onSuccess?.(data, ...rest); + setText(data); + onTextChange?.(data); + handleStop(); + onFinished?.(data, ...rest); + }, + }); return { blob, diff --git a/src/react/useOpenaiTTS/index.ts b/src/react/useOpenaiTTS/index.ts index 74ae72a..66648f5 100644 --- a/src/react/useOpenaiTTS/index.ts +++ b/src/react/useOpenaiTTS/index.ts @@ -1,12 +1,19 @@ import { useState } from 'react'; -import { useTTS } from '@/react/useTTS'; +import { TTSConfig, useTTS } from '@/react/useTTS'; import { type OpenaiTtsOptions, fetchOpenaiTTS } from '@/services/fetchOpenaiTTS'; -export const useOpenaiTTS = (defaultText: string, options: OpenaiTtsOptions) => { +export const useOpenaiTTS = ( + defaultText: string, + options: OpenaiTtsOptions, + config?: TTSConfig, +) => { const [text, setText] = useState(defaultText); - const rest = useTTS(options.voice, text, (segmentText: string) => - fetchOpenaiTTS(segmentText, options), + const rest = useTTS( + options.voice, + text, + (segmentText: string) => fetchOpenaiTTS(segmentText, options), + config, ); return { setText, diff --git a/src/react/useTTS/index.ts b/src/react/useTTS/index.ts index a922fcb..48be2bb 100644 --- a/src/react/useTTS/index.ts +++ b/src/react/useTTS/index.ts @@ -1,11 +1,11 @@ import { useCallback, useEffect, useState } from 'react'; -import useSWR from 'swr'; +import useSWR, { type SWRConfiguration } from 'swr'; import { AudioProps } from '@/react/AudioPlayer'; import { useStreamAudioPlayer } from '@/react/hooks/useStreamAudioPlayer'; import { splitTextIntoSegments } from '@/utils/splitTextIntoSegments'; -export interface TTSHook { +export interface TTSHook extends SWRConfiguration { audio: AudioProps; isGlobalLoading: boolean; isLoading: boolean; @@ -13,10 +13,17 @@ export interface TTSHook { stop: () => void; } +export interface TTSConfig extends SWRConfiguration { + onFinish?: SWRConfiguration['onSuccess']; + onStart?: () => void; + onStop?: () => void; +} + export const useTTS = ( key: string, text: string, fetchTTS: (segmentText: string) => Promise, + { onError, onSuccess, onFinish, onStart, onStop, ...restSWRConfig }: TTSConfig = {}, ): TTSHook => { const { load, reset, ...rest } = useStreamAudioPlayer(); const [shouldFetch, setShouldFetch] = useState(false); @@ -33,6 +40,7 @@ export const useTTS = ( }, []); const handleStop = useCallback(() => { + onStop?.(); handleReset(); }, []); @@ -40,24 +48,29 @@ export const useTTS = ( shouldFetch && textArray?.length > 0 ? [key, textArray?.[index]] : null, async () => await fetchTTS(textArray[index]), { - onError: (err) => { + onError: (err, ...rest) => { + onError?.(err, ...rest); console.error(err); handleReset(); }, - onSuccess: (data) => { + onSuccess: (data, ...rest) => { + onSuccess?.(data, ...rest); load(data); if (index < textArray.length - 1) { setIndex(index + 1); } else { + onFinish?.(data, ...rest); setShouldFetch(false); setIsGlobalLoading(false); } }, + ...restSWRConfig, }, ); const handleStart = useCallback(() => { if (isLoading) return; + onStart?.(); reset(); setShouldFetch(true); setIsGlobalLoading(true); From fbc6cfe979e62ccc860aec0b0842ca7cc475208d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 14:51:43 +0000 Subject: [PATCH 22/24] :bookmark: chore(release): v1.13.0-beta.2 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [Version 1.13.0-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.13.0-beta.1...v1.13.0-beta.2) Released on **2023-11-15** #### ✨ Features - **misc**: Add SWR config to hooks.
Improvements and Fixes #### What's improved * **misc**: Add SWR config to hooks ([2c49e02](https://github.com/lobehub/lobe-tts/commit/2c49e02))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61592cc..4abead3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +## [Version 1.13.0-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.13.0-beta.1...v1.13.0-beta.2) + +Released on **2023-11-15** + +#### ✨ Features + +- **misc**: Add SWR config to hooks. + +
+ +
+Improvements and Fixes + +#### What's improved + +- **misc**: Add SWR config to hooks ([2c49e02](https://github.com/lobehub/lobe-tts/commit/2c49e02)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ## [Version 1.13.0-beta.1](https://github.com/lobehub/lobe-tts/compare/v1.12.1-beta.6...v1.13.0-beta.1) Released on **2023-11-15** diff --git a/package.json b/package.json index e8a82f2..e6ab8d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.13.0-beta.1", + "version": "1.13.0-beta.2", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": { From 802c59a5e2508c03630ca25b71663bcde6076f63 Mon Sep 17 00:00:00 2001 From: arvinxx Date: Wed, 15 Nov 2023 22:56:52 +0800 Subject: [PATCH 23/24] :recycle: refactor: refactor the demo usage --- src/class/EdgeSpeechTTS.ts | 10 ++++----- src/class/MicrosoftSpeechTTS.ts | 6 +++--- src/class/OpenaiTTS.ts | 6 +++++- src/index.ts | 22 +++++--------------- src/react/_util/leva.ts | 7 +++++++ src/react/useEdgeSpeech/demos/index.tsx | 6 ++++-- src/react/useMicrosoftSpeech/demos/index.tsx | 6 ++++-- src/react/useOpenaiTTS/demos/index.tsx | 4 ++-- src/react/useSpeechSynthes/demos/index.tsx | 4 +++- src/utils/getVoiceList.ts | 6 ------ 10 files changed, 38 insertions(+), 39 deletions(-) create mode 100644 src/react/_util/leva.ts diff --git a/src/class/EdgeSpeechTTS.ts b/src/class/EdgeSpeechTTS.ts index 895bcac..3bc721b 100644 --- a/src/class/EdgeSpeechTTS.ts +++ b/src/class/EdgeSpeechTTS.ts @@ -8,13 +8,13 @@ export class EdgeSpeechTTS { constructor(locale?: string) { this.locale = locale; } + get voiceOptions() { return getEdgeVoiceOptions(this.locale); } - get localeOptions() { - return getVoiceLocaleOptions(); - } - voiceList = edgeVoiceList; - voiceName = voiceName; + + static localeOptions = getVoiceLocaleOptions(); + static voiceList = edgeVoiceList; + static voiceName = voiceName; fetch = fetchEdgeSpeech; } diff --git a/src/class/MicrosoftSpeechTTS.ts b/src/class/MicrosoftSpeechTTS.ts index 2362078..414be55 100644 --- a/src/class/MicrosoftSpeechTTS.ts +++ b/src/class/MicrosoftSpeechTTS.ts @@ -11,9 +11,9 @@ export class MicrosoftSpeechTTS { get voiceOptions() { return getAzureVoiceOptions(this.locale); } - get localeOptions() { - return getVoiceLocaleOptions(); - } + + static localeOptions = getVoiceLocaleOptions(); + voiceList = azureVoiceList; voiceName = voiceName; fetch = fetchMicrosoftSpeech; diff --git a/src/class/OpenaiTTS.ts b/src/class/OpenaiTTS.ts index cf7b555..4edd084 100644 --- a/src/class/OpenaiTTS.ts +++ b/src/class/OpenaiTTS.ts @@ -3,12 +3,16 @@ import { fetchOpenaiTTS } from '@/services/fetchOpenaiTTS'; import { getOpenaiVoiceOptions, getVoiceLocaleOptions } from '@/utils/getVoiceList'; export class OpenaiTTS { + static voiceList = openaiVoiceList; + get voiceOptions() { return getOpenaiVoiceOptions(); } get localeOptions() { return getVoiceLocaleOptions(); } - voiceList = openaiVoiceList; + + static localeOptions = getVoiceLocaleOptions(); + fetch = fetchOpenaiTTS; } diff --git a/src/index.ts b/src/index.ts index c026470..0f034cc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,24 +3,12 @@ export { MicrosoftSpeechTTS } from './class/MicrosoftSpeechTTS'; export { OpenaiSTT } from './class/OpenaiSTT'; export { OpenaiTTS } from './class/OpenaiTTS'; export { VoiceList } from './class/VoiceList'; -export { default as azureVoiceList } from './data/azureVoiceList'; -export { default as edgeVoiceList } from './data/edgeVoiceList'; -export { default as voiceLocale } from './data/locales'; -export { default as openaiVoiceList } from './data/openaiVoiceList'; -export { default as voiceList } from './data/voiceList'; -export { type EdgeSpeechOptions, fetchEdgeSpeech } from './services/fetchEdgeSpeech'; -export { fetchMicrosoftSpeech, type MicrosoftSpeechOptions } from './services/fetchMicrosoftSpeech'; -export { fetchOpenaiSTT, type OpenaiSttOptions } from './services/fetchOpenaiSTT'; -export { fetchOpenaiTTS, type OpenaiTtsOptions } from './services/fetchOpenaiTTS'; +export { type EdgeSpeechOptions } from './services/fetchEdgeSpeech'; +export { type MicrosoftSpeechOptions } from './services/fetchMicrosoftSpeech'; +export { type OpenaiSttOptions } from './services/fetchOpenaiSTT'; +export { type OpenaiTtsOptions } from './services/fetchOpenaiTTS'; export { getRecordMineType, type RecordMineType } from './utils/getRecordMineType'; -export { - genLevaOptions, - getAzureVoiceOptions, - getEdgeVoiceOptions, - getOpenaiVoiceOptions, - getSpeechSynthesVoiceOptions, - getVoiceLocaleOptions, -} from './utils/getVoiceList'; +export { getSpeechSynthesVoiceOptions } from './utils/getVoiceList'; export { EDGE_SPEECH_API_URL, MICROSOFT_SPEECH_API_URL, diff --git a/src/react/_util/leva.ts b/src/react/_util/leva.ts new file mode 100644 index 0000000..1e54a9a --- /dev/null +++ b/src/react/_util/leva.ts @@ -0,0 +1,7 @@ +import { SelectProps } from 'antd'; + +export const genLevaOptions = (options: SelectProps['options']) => { + const data: any = {}; + options?.forEach((item: any) => (data[item?.label || item?.value] = item?.value)); + return data; +}; diff --git a/src/react/useEdgeSpeech/demos/index.tsx b/src/react/useEdgeSpeech/demos/index.tsx index eefc251..a01234b 100644 --- a/src/react/useEdgeSpeech/demos/index.tsx +++ b/src/react/useEdgeSpeech/demos/index.tsx @@ -1,10 +1,12 @@ -import { EDGE_SPEECH_API_URL, genLevaOptions, getEdgeVoiceOptions } from '@lobehub/tts'; +import { EDGE_SPEECH_API_URL, EdgeSpeechTTS } from '@lobehub/tts'; import { AudioPlayer, useEdgeSpeech } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; import { Flexbox } from 'react-layout-kit'; +import { genLevaOptions } from '../../_util/leva'; + const defaultText = '这是一段使用 Edge Speech 的语音演示'; export default () => { @@ -20,7 +22,7 @@ export default () => { const options: any = useControls( { voice: { - options: genLevaOptions(getEdgeVoiceOptions()), + options: genLevaOptions(new EdgeSpeechTTS().voiceOptions), value: 'zh-CN-YunxiaNeural', }, }, diff --git a/src/react/useMicrosoftSpeech/demos/index.tsx b/src/react/useMicrosoftSpeech/demos/index.tsx index a93254f..756866c 100644 --- a/src/react/useMicrosoftSpeech/demos/index.tsx +++ b/src/react/useMicrosoftSpeech/demos/index.tsx @@ -1,10 +1,12 @@ -import { MICROSOFT_SPEECH_API_URL, genLevaOptions, getEdgeVoiceOptions } from '@lobehub/tts'; +import { MICROSOFT_SPEECH_API_URL, MicrosoftSpeechTTS } from '@lobehub/tts'; import { AudioPlayer, useMicrosoftSpeech } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { Volume2 } from 'lucide-react'; import { Flexbox } from 'react-layout-kit'; +import { genLevaOptions } from '../../_util/leva'; + const defaultText = '这是一段使用 Microsoft Speech 的语音演示'; export default () => { @@ -49,7 +51,7 @@ export default () => { value: 'general', }, voice: { - options: genLevaOptions(getEdgeVoiceOptions()), + options: genLevaOptions(new MicrosoftSpeechTTS().voiceOptions), value: 'zh-CN-YunxiaNeural', }, }, diff --git a/src/react/useOpenaiTTS/demos/index.tsx b/src/react/useOpenaiTTS/demos/index.tsx index 8d923d1..5cdc0be 100644 --- a/src/react/useOpenaiTTS/demos/index.tsx +++ b/src/react/useOpenaiTTS/demos/index.tsx @@ -1,4 +1,4 @@ -import { OPENAI_TTS_API_URL, openaiVoiceList } from '@lobehub/tts'; +import { OPENAI_TTS_API_URL, OpenaiTTS } from '@lobehub/tts'; import { AudioPlayer, useOpenaiTTS } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; @@ -28,7 +28,7 @@ export default () => { const options: any = useControls( { voice: { - options: openaiVoiceList, + options: OpenaiTTS.voiceList, value: 'alloy', }, }, diff --git a/src/react/useSpeechSynthes/demos/index.tsx b/src/react/useSpeechSynthes/demos/index.tsx index 8eba110..9ea314a 100644 --- a/src/react/useSpeechSynthes/demos/index.tsx +++ b/src/react/useSpeechSynthes/demos/index.tsx @@ -1,10 +1,12 @@ -import { genLevaOptions, getSpeechSynthesVoiceOptions } from '@lobehub/tts'; +import { getSpeechSynthesVoiceOptions } from '@lobehub/tts'; import { useSpeechSynthes } from '@lobehub/tts/react'; import { Icon, StoryBook, useControls, useCreateStore } from '@lobehub/ui'; import { Button, Input } from 'antd'; import { StopCircle, Volume2 } from 'lucide-react'; import { Flexbox } from 'react-layout-kit'; +import { genLevaOptions } from '../../_util/leva'; + const defaultText = '这是一段使用 Speech Synthes 的语音演示'; export default () => { diff --git a/src/utils/getVoiceList.ts b/src/utils/getVoiceList.ts index 9cd789b..35e5c7b 100644 --- a/src/utils/getVoiceList.ts +++ b/src/utils/getVoiceList.ts @@ -56,9 +56,3 @@ export const getOpenaiVoiceOptions = (): SelectProps['options'] => { export const getVoiceLocaleOptions = (): SelectProps['options'] => { return Object.entries(voiceLocale).map(([value, label]) => ({ label, value })); }; - -export const genLevaOptions = (options: SelectProps['options']) => { - const data: any = {}; - options?.forEach((item: any) => (data[item?.label || item?.value] = item?.value)); - return data; -}; From 1a730722081e2a29befe938fea9f4d0c6f2075ac Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 15 Nov 2023 14:58:13 +0000 Subject: [PATCH 24/24] :bookmark: chore(release): v1.13.0-beta.3 [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [Version 1.13.0-beta.3](https://github.com/lobehub/lobe-tts/compare/v1.13.0-beta.2...v1.13.0-beta.3) Released on **2023-11-15** #### ♻ Code Refactoring - **misc**: Refactor the demo usage.
Improvements and Fixes #### Code refactoring * **misc**: Refactor the demo usage ([802c59a](https://github.com/lobehub/lobe-tts/commit/802c59a))
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
--- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4abead3..1f51e45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ # Changelog +## [Version 1.13.0-beta.3](https://github.com/lobehub/lobe-tts/compare/v1.13.0-beta.2...v1.13.0-beta.3) + +Released on **2023-11-15** + +#### ♻ Code Refactoring + +- **misc**: Refactor the demo usage. + +
+ +
+Improvements and Fixes + +#### Code refactoring + +- **misc**: Refactor the demo usage ([802c59a](https://github.com/lobehub/lobe-tts/commit/802c59a)) + +
+ +
+ +[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) + +
+ ## [Version 1.13.0-beta.2](https://github.com/lobehub/lobe-tts/compare/v1.13.0-beta.1...v1.13.0-beta.2) Released on **2023-11-15** diff --git a/package.json b/package.json index e6ab8d5..4be404d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lobehub/tts", - "version": "1.13.0-beta.2", + "version": "1.13.0-beta.3", "description": "A high-quality & reliable TTS React Hooks library", "homepage": "https://github.com/lobehub/lobe-tts", "bugs": {