mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 19:59:50 +08:00
5bd432a85f
Co-authored-by: luowei <glpat-EjySCyNjWiLqAED-YmwM> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
160 lines
4.5 KiB
TypeScript
160 lines
4.5 KiB
TypeScript
'use client'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import { t } from 'i18next'
|
|
import { useParams, usePathname } from 'next/navigation'
|
|
import s from './style.module.css'
|
|
import Tooltip from '@/app/components/base/tooltip'
|
|
import { randomString } from '@/utils'
|
|
import { textToAudio } from '@/service/share'
|
|
import Loading from '@/app/components/base/loading'
|
|
|
|
type AudioBtnProps = {
|
|
value: string
|
|
voice?: string
|
|
className?: string
|
|
isAudition?: boolean
|
|
noCache: boolean
|
|
}
|
|
|
|
type AudioState = 'initial' | 'loading' | 'playing' | 'paused' | 'ended'
|
|
|
|
const AudioBtn = ({
|
|
value,
|
|
voice,
|
|
className,
|
|
isAudition,
|
|
noCache,
|
|
}: AudioBtnProps) => {
|
|
const audioRef = useRef<HTMLAudioElement | null>(null)
|
|
const [audioState, setAudioState] = useState<AudioState>('initial')
|
|
|
|
const selector = useRef(`play-tooltip-${randomString(4)}`)
|
|
const params = useParams()
|
|
const pathname = usePathname()
|
|
const removeCodeBlocks = (inputText: any) => {
|
|
const codeBlockRegex = /```[\s\S]*?```/g
|
|
if (inputText)
|
|
return inputText.replace(codeBlockRegex, '')
|
|
return ''
|
|
}
|
|
|
|
const loadAudio = async () => {
|
|
const formData = new FormData()
|
|
formData.append('text', removeCodeBlocks(value))
|
|
formData.append('voice', removeCodeBlocks(voice))
|
|
|
|
if (value !== '') {
|
|
setAudioState('loading')
|
|
|
|
let url = ''
|
|
let isPublic = false
|
|
|
|
if (params.token) {
|
|
url = '/text-to-audio'
|
|
isPublic = true
|
|
}
|
|
else if (params.appId) {
|
|
if (pathname.search('explore/installed') > -1)
|
|
url = `/installed-apps/${params.appId}/text-to-audio`
|
|
else
|
|
url = `/apps/${params.appId}/text-to-audio`
|
|
}
|
|
|
|
try {
|
|
const audioResponse = await textToAudio(url, isPublic, formData)
|
|
const blob_bytes = Buffer.from(audioResponse.data, 'latin1')
|
|
const blob = new Blob([blob_bytes], { type: 'audio/wav' })
|
|
const audioUrl = URL.createObjectURL(blob)
|
|
audioRef.current!.src = audioUrl
|
|
}
|
|
catch (error) {
|
|
setAudioState('initial')
|
|
console.error('Error playing audio:', error)
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleToggle = async () => {
|
|
if (audioState === 'initial' || noCache) {
|
|
await loadAudio()
|
|
}
|
|
else if (audioRef.current) {
|
|
if (audioState === 'playing') {
|
|
audioRef.current.pause()
|
|
setAudioState('paused')
|
|
}
|
|
else {
|
|
audioRef.current.play()
|
|
setAudioState('playing')
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
const currentAudio = audioRef.current
|
|
|
|
const handleLoading = () => {
|
|
setAudioState('loading')
|
|
}
|
|
|
|
const handlePlay = () => {
|
|
currentAudio?.play()
|
|
setAudioState('playing')
|
|
}
|
|
|
|
const handleEnded = () => {
|
|
setAudioState('ended')
|
|
}
|
|
|
|
currentAudio?.addEventListener('progress', handleLoading)
|
|
currentAudio?.addEventListener('canplaythrough', handlePlay)
|
|
currentAudio?.addEventListener('ended', handleEnded)
|
|
|
|
return () => {
|
|
currentAudio?.removeEventListener('progress', handleLoading)
|
|
currentAudio?.removeEventListener('canplaythrough', handlePlay)
|
|
currentAudio?.removeEventListener('ended', handleEnded)
|
|
URL.revokeObjectURL(currentAudio?.src || '')
|
|
currentAudio?.pause()
|
|
currentAudio?.setAttribute('src', '')
|
|
}
|
|
}, [])
|
|
|
|
const tooltipContent = {
|
|
initial: t('appApi.play'),
|
|
ended: t('appApi.play'),
|
|
paused: t('appApi.pause'),
|
|
playing: t('appApi.playing'),
|
|
loading: t('appApi.loading'),
|
|
}[audioState]
|
|
|
|
return (
|
|
<div className={`${(audioState === 'loading' || audioState === 'playing') ? 'mr-1' : className}`}>
|
|
<Tooltip
|
|
selector={selector.current}
|
|
content={tooltipContent}
|
|
className='z-10'
|
|
>
|
|
<button
|
|
disabled={audioState === 'loading'}
|
|
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`}
|
|
onClick={handleToggle}
|
|
>
|
|
{audioState === 'loading'
|
|
? (
|
|
<div className='w-6 h-6 rounded-md flex items-center justify-center p-2'>
|
|
<Loading />
|
|
</div>
|
|
)
|
|
: (
|
|
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(audioState === 'playing') ? s.pauseIcon : s.playIcon}`}></div>
|
|
)}
|
|
</button>
|
|
</Tooltip>
|
|
<audio ref={audioRef} src='' className='hidden' />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default AudioBtn
|