diff --git a/README.md b/README.md
index ecb73a3..1d3b7e6 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,8 @@ A high-quality & reliable TTS/STT library for Server and Browser
[![][github-forks-shield]][github-forks-link]
[![][github-stars-shield]][github-stars-link]
[![][github-issues-shield]][github-issues-link]
-[![][github-license-shield]][github-license-link]
+[![][github-license-shield]][github-license-link]
+[![][sponsor-shield]][sponsor-link]
[Changelog](./CHANGELOG.md) · [Report Bug][github-issues-link] · [Request Feature][github-issues-link]
@@ -37,6 +38,7 @@ A high-quality & reliable TTS/STT library for Server and Browser
- [Compile with Next.js](#compile-with-nextjs)
- [⌨️ Local Development](#️-local-development)
- [🤝 Contributing](#-contributing)
+- [🩷 Sponsor](#-sponsor)
- [🔗 More Products](#-more-products)
####
@@ -195,6 +197,23 @@ Contributions of all types are more than welcome, if you are interested in contr
+## 🩷 Sponsor
+
+Every bit counts and your one-time donation sparkles in our galaxy of support! You're a shooting star, making a swift and bright impact on our journey. Thank you for believing in us – your generosity guides us toward our mission, one brilliant flash at a time.
+
+
+
+
+
+
+
+
+
+
+[![][back-to-top]](#readme-top)
+
+
+
## 🔗 More Products
- **[🤖 Lobe Chat](https://github.com/lobehub/lobe-chat)** - An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application.
@@ -241,3 +260,5 @@ This project is [MIT](./LICENSE) licensed.
[pr-welcome-link]: https://github.com/lobehub/lobe-tts/pulls
[pr-welcome-shield]: https://img.shields.io/badge/%F0%9F%A4%AF%20PR%20WELCOME-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
[profile-link]: https://github.com/lobehub
+[sponsor-link]: https://opencollective.com/lobehub 'Become 🩷 LobeHub Sponsor'
+[sponsor-shield]: https://img.shields.io/badge/-Sponsor%20LobeHub-f04f88?logo=opencollective&logoColor=white&style=flat-square
diff --git a/src/core/index.ts b/src/core/index.ts
index 95def0b..7d61d38 100644
--- a/src/core/index.ts
+++ b/src/core/index.ts
@@ -12,5 +12,6 @@ export {
type OpenaiVoice,
} from '@/core/OpenAITTS';
export { SpeechSynthesisTTS } from '@/core/SpeechSynthesisTTS';
+export { cleanContent } from '@/core/utils/cleanContent';
export { getRecordMineType, type RecordMineType } from '@/core/utils/getRecordMineType';
export { VoiceList } from '@/core/VoiceList';
diff --git a/src/react/AudioPlayer/demos/index.tsx b/src/react/AudioPlayer/demos/index.tsx
index 74b418c..2ac411e 100644
--- a/src/react/AudioPlayer/demos/index.tsx
+++ b/src/react/AudioPlayer/demos/index.tsx
@@ -25,7 +25,7 @@ export default () => {
return (
-
+
);
};
diff --git a/src/react/AudioPlayer/index.tsx b/src/react/AudioPlayer/index.tsx
index 597d8be..b1c0920 100644
--- a/src/react/AudioPlayer/index.tsx
+++ b/src/react/AudioPlayer/index.tsx
@@ -20,6 +20,7 @@ export interface AudioProps {
export interface AudioPlayerProps {
allowPause?: boolean;
audio: AudioProps;
+ autoplay?: boolean;
buttonActive?: boolean;
buttonSize?: ActionIconProps['size'];
buttonStyle?: CSSProperties;
@@ -48,6 +49,7 @@ const AudioPlayer = memo(
className,
onLoadingStop,
audio = {
+ canPlay: false,
currentTime: 0,
download: () => {},
duration: 0,
diff --git a/src/react/hooks/useAudioPlayer.ts b/src/react/hooks/useAudioPlayer.ts
index bcd9838..4c26182 100644
--- a/src/react/hooks/useAudioPlayer.ts
+++ b/src/react/hooks/useAudioPlayer.ts
@@ -3,7 +3,7 @@ import useSWR from 'swr';
import { AudioProps } from '@/react/AudioPlayer';
-export interface AudioPlayerReturn extends AudioProps {
+export interface AudioPlayerResponse extends AudioProps {
arrayBuffers: ArrayBuffer[];
download: () => void;
isLoading?: boolean;
@@ -20,43 +20,43 @@ export interface AudioPlayerOptions {
export const useAudioPlayer = ({
src,
type = 'audio/mp3',
-}: AudioPlayerOptions = {}): AudioPlayerReturn => {
- const audioRef = useRef(new Audio());
+}: AudioPlayerOptions = {}): AudioPlayerResponse => {
+ const audioRef = useRef();
const [arrayBuffers, setArrayBuffers] = useState([]);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [isGlobalLoading, setIsGlobalLoading] = useState(true);
- const { isLoading } = useSWR(
- src || null,
- async () => {
- if (!src) return;
- setIsGlobalLoading(true);
- const data = await fetch(src);
- const arrayBuffer = await data.arrayBuffer();
- setArrayBuffers([arrayBuffer]);
- const newBlob = new Blob([arrayBuffer], { type: type });
- audioRef.current.pause();
- audioRef.current.currentTime = 0;
- if (audioRef.current.src) URL.revokeObjectURL(audioRef.current.src);
- audioRef.current.src = URL.createObjectURL(newBlob);
- audioRef.current.load();
- },
- { revalidateOnFocus: false },
- );
+ const { isLoading } = useSWR(src || null, async () => {
+ if (!src) return;
+ setIsGlobalLoading(true);
+ const data = await fetch(src);
+ const arrayBuffer = await data.arrayBuffer();
+ setArrayBuffers([arrayBuffer]);
+ const newBlob = new Blob([arrayBuffer], { type: type });
+ if (!audioRef.current) audioRef.current = new Audio();
+ audioRef.current.pause();
+ audioRef.current.currentTime = 0;
+ if (audioRef.current.src) URL.revokeObjectURL(audioRef.current.src);
+ audioRef.current.src = URL.createObjectURL(newBlob);
+ audioRef.current.load();
+ });
useEffect(() => {
- if (!audioRef.current) return;
+ if (!audioRef.current) audioRef.current = new Audio();
const onLoadedMetadata = () => {
+ if (!audioRef.current) return;
setDuration(audioRef.current.duration);
setIsGlobalLoading(false);
};
const onTimeUpdate = () => {
+ if (!audioRef.current) return;
setCurrentTime(audioRef.current.currentTime);
};
const onEnded = async () => {
+ if (!audioRef.current) return;
setIsPlaying(false);
audioRef.current.currentTime = 0;
setCurrentTime(0);
@@ -68,6 +68,7 @@ export const useAudioPlayer = ({
audioRef.current.addEventListener('timeupdate', onTimeUpdate);
return () => {
+ if (!audioRef.current) return;
audioRef.current.pause();
audioRef.current.load();
audioRef.current.removeEventListener('ended', onEnded);
@@ -79,27 +80,38 @@ export const useAudioPlayer = ({
}, []);
const handlePlay = useCallback(() => {
- setIsPlaying(true);
- audioRef.current.play();
+ try {
+ if (!audioRef.current) return;
+ setIsPlaying(true);
+ audioRef.current?.play();
+ } catch {
+ setTimeout(() => {
+ handlePlay();
+ }, 200);
+ }
}, []);
const handlePause = useCallback(() => {
+ if (!audioRef.current) return;
setIsPlaying(false);
audioRef.current.pause();
}, []);
const handleStop = useCallback(() => {
+ if (!audioRef.current) return;
setIsPlaying(false);
audioRef.current.pause();
audioRef.current.currentTime = 0;
}, []);
const setTime = useCallback((value: number) => {
+ if (!audioRef.current) return;
setCurrentTime(value);
audioRef.current.currentTime = value;
}, []);
const reset = useCallback(() => {
+ if (!audioRef.current) return;
audioRef.current.pause();
audioRef.current.currentTime = 0;
if (audioRef.current.src) URL.revokeObjectURL(audioRef.current.src);
@@ -109,6 +121,7 @@ export const useAudioPlayer = ({
}, []);
const handleDownload = useCallback(async () => {
+ if (!audioRef.current) return;
const a = document.createElement('a');
a.href = audioRef.current.src;
a.download = 'audio.mp3';
@@ -124,10 +137,10 @@ export const useAudioPlayer = ({
isPlaying,
pause: handlePause,
play: handlePlay,
- ref: audioRef,
+ ref: audioRef as any,
reset,
setTime,
stop: handleStop,
- url: audioRef.current.src,
+ url: audioRef?.current?.src || '',
};
};
diff --git a/src/react/hooks/useBlobUrl.ts b/src/react/hooks/useBlobUrl.ts
index 752f7ac..f91cb05 100644
--- a/src/react/hooks/useBlobUrl.ts
+++ b/src/react/hooks/useBlobUrl.ts
@@ -33,7 +33,6 @@ export const useBlobUrl = (src: string) => {
} catch {}
setIsGlobalLoading(false);
},
- revalidateOnFocus: false,
},
);
diff --git a/src/react/hooks/useStreamAudioPlayer.ts b/src/react/hooks/useStreamAudioPlayer.ts
index d2c79eb..d42ab90 100644
--- a/src/react/hooks/useStreamAudioPlayer.ts
+++ b/src/react/hooks/useStreamAudioPlayer.ts
@@ -2,7 +2,7 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { AudioProps } from '@/react/AudioPlayer';
-export interface StreamAudioPlayerReturn extends AudioProps {
+export interface StreamAudioPlayerResponse extends AudioProps {
arrayBuffers: ArrayBuffer[];
download: () => void;
load: (arrayBuffer: ArrayBuffer) => void;
@@ -11,7 +11,7 @@ export interface StreamAudioPlayerReturn extends AudioProps {
url: string;
}
-export const useStreamAudioPlayer = (): StreamAudioPlayerReturn => {
+export const useStreamAudioPlayer = (): StreamAudioPlayerResponse => {
const audioRef = useRef();
const [arrayBuffers, setArrayBuffers] = useState([]);
const [currentTime, setCurrentTime] = useState(0);
diff --git a/src/react/index.ts b/src/react/index.ts
index 0484b33..5c7ae31 100644
--- a/src/react/index.ts
+++ b/src/react/index.ts
@@ -1,6 +1,6 @@
export { default as AudioPlayer, type AudioPlayerProps } from './AudioPlayer';
export { default as AudioVisualizer, type AudioVisualizerProps } from './AudioVisualizer';
-export { type AudioPlayerReturn, useAudioPlayer } from './hooks/useAudioPlayer';
+export { type AudioPlayerResponse, useAudioPlayer } from './hooks/useAudioPlayer';
export { useAudioVisualizer } from './hooks/useAudioVisualizer';
export { useBlobUrl } from './hooks/useBlobUrl';
export { useStreamAudioPlayer } from './hooks/useStreamAudioPlayer';
@@ -11,4 +11,4 @@ export { type OpenAISTTOptions, useOpenAISTT } from './useOpenAISTT';
export { type OpenAITTSOptions, useOpenAITTS } from './useOpenAITTS';
export { type SpeechRecognitionOptions, useSpeechRecognition } from './useSpeechRecognition';
export { type SpeechSynthesOptions, useSpeechSynthes } from './useSpeechSynthes';
-export { type TTSHook } from './useTTS';
+export { type TTSOptions } from './useTTS';
diff --git a/src/react/useEdgeSpeech/index.ts b/src/react/useEdgeSpeech/index.ts
index 4287f79..e44d687 100644
--- a/src/react/useEdgeSpeech/index.ts
+++ b/src/react/useEdgeSpeech/index.ts
@@ -1,9 +1,9 @@
import { useState } from 'react';
import { type EdgeSpeechAPI, type EdgeSpeechPayload, EdgeSpeechTTS } from '@/core/EdgeSpeechTTS';
-import { type TTSConfig, useTTS } from '@/react/useTTS';
+import { type TTSOptions, useTTS } from '@/react/useTTS';
-export interface EdgeSpeechOptions extends Pick, TTSConfig {
+export interface EdgeSpeechOptions extends Pick, TTSOptions {
api?: EdgeSpeechAPI;
locale?: string;
}
diff --git a/src/react/useMicrosoftSpeech/index.ts b/src/react/useMicrosoftSpeech/index.ts
index 4350ac1..e97e8ee 100644
--- a/src/react/useMicrosoftSpeech/index.ts
+++ b/src/react/useMicrosoftSpeech/index.ts
@@ -5,9 +5,11 @@ import {
type MicrosoftSpeechPayload,
MicrosoftSpeechTTS,
} from '@/core/MicrosoftSpeechTTS';
-import { type TTSConfig, useTTS } from '@/react/useTTS';
+import { type TTSOptions, useTTS } from '@/react/useTTS';
-export interface MicrosoftSpeechOptions extends Pick, TTSConfig {
+export interface MicrosoftSpeechOptions
+ extends Pick,
+ TTSOptions {
api?: MicrosoftSpeechAPI;
locale?: string;
}
diff --git a/src/react/useOpenAISTT/useOpenAISTTCore.ts b/src/react/useOpenAISTT/useOpenAISTTCore.ts
index 4c912ad..633be6e 100644
--- a/src/react/useOpenAISTT/useOpenAISTTCore.ts
+++ b/src/react/useOpenAISTT/useOpenAISTTCore.ts
@@ -16,6 +16,6 @@ export const useOpenAISTTCore = (init: OpenAISTTCoreOptions) => {
const instance = new OpenaiSTT(api);
return instance.create({ options, speech });
},
- { revalidateOnFocus: false, ...swrConfig },
+ swrConfig,
);
};
diff --git a/src/react/useOpenAITTS/index.ts b/src/react/useOpenAITTS/index.ts
index e4fabf7..822ef5c 100644
--- a/src/react/useOpenAITTS/index.ts
+++ b/src/react/useOpenAITTS/index.ts
@@ -1,9 +1,9 @@
import { useState } from 'react';
import { OpenAITTS, type OpenAITTSAPI, type OpenAITTSPayload } from '@/core/OpenAITTS';
-import { type TTSConfig, useTTS } from '@/react/useTTS';
+import { type TTSOptions, useTTS } from '@/react/useTTS';
-export interface OpenAITTSOptions extends Pick, TTSConfig {
+export interface OpenAITTSOptions extends Pick, TTSOptions {
api?: OpenAITTSAPI;
}
diff --git a/src/react/useTTS/index.ts b/src/react/useTTS/index.ts
index e78af98..db23f3b 100644
--- a/src/react/useTTS/index.ts
+++ b/src/react/useTTS/index.ts
@@ -1,12 +1,11 @@
import { useCallback, useEffect, useState } from 'react';
import useSWR, { type SWRConfiguration, type SWRResponse } from 'swr';
-import { cleanContent } from '@/core/utils/cleanContent';
import { splitTextIntoSegments } from '@/core/utils/splitTextIntoSegments';
import { type AudioProps } from '@/react/AudioPlayer';
import { useStreamAudioPlayer } from '@/react/hooks/useStreamAudioPlayer';
-export interface TTSHook extends SWRConfiguration, Pick {
+export interface TTSResponse extends SWRConfiguration, Pick {
audio: AudioProps & {
arrayBuffers: ArrayBuffer[];
};
@@ -17,7 +16,7 @@ export interface TTSHook extends SWRConfiguration, Pick void;
}
-export interface TTSConfig extends SWRConfiguration {
+export interface TTSOptions extends SWRConfiguration {
onFinish?: SWRConfiguration['onSuccess'];
onStart?: () => void;
onStop?: () => void;
@@ -27,8 +26,8 @@ export const useTTS = (
key: string,
text: string,
fetchTTS: (segmentText: string) => Promise,
- { onError, onSuccess, onFinish, onStart, onStop, ...restSWRConfig }: TTSConfig = {},
-): TTSHook => {
+ { onError, onSuccess, onFinish, onStart, onStop, ...restSWRConfig }: TTSOptions = {},
+): TTSResponse => {
const [shouldFetch, setShouldFetch] = useState(false);
const [isGlobalLoading, setIsGlobalLoading] = useState(false);
const [index, setIndex] = useState(0);
@@ -68,7 +67,6 @@ export const useTTS = (
setIsGlobalLoading(false);
}
},
- revalidateOnFocus: false,
...restSWRConfig,
},
);
@@ -82,10 +80,8 @@ export const useTTS = (
}, [text, isLoading]);
useEffect(() => {
- cleanContent(text).then((content) => {
- const texts = splitTextIntoSegments(content);
- handleReset(texts);
- });
+ const texts = splitTextIntoSegments(text);
+ handleReset(texts);
return () => {
handleReset();
};