123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- '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
|