From 1fafadcdc282c056697b6d3d26e92962a9580fd5 Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 6 Dec 2023 21:48:10 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Fix=20autoplay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 23 +++++++- src/core/index.ts | 1 + src/react/AudioPlayer/demos/index.tsx | 2 +- src/react/AudioPlayer/index.tsx | 2 + src/react/hooks/useAudioPlayer.ts | 63 +++++++++++++--------- src/react/hooks/useBlobUrl.ts | 1 - src/react/hooks/useStreamAudioPlayer.ts | 4 +- src/react/index.ts | 4 +- src/react/useEdgeSpeech/index.ts | 4 +- src/react/useMicrosoftSpeech/index.ts | 6 ++- src/react/useOpenAISTT/useOpenAISTTCore.ts | 2 +- src/react/useOpenAITTS/index.ts | 4 +- src/react/useTTS/index.ts | 16 +++--- 13 files changed, 83 insertions(+), 49 deletions(-) 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(); };