index.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. 'use client'
  2. import { useRef, useState } from 'react'
  3. import { t } from 'i18next'
  4. import { useParams, usePathname } from 'next/navigation'
  5. import s from './style.module.css'
  6. import Tooltip from '@/app/components/base/tooltip'
  7. import { randomString } from '@/utils'
  8. import { textToAudio } from '@/service/share'
  9. type AudioBtnProps = {
  10. value: string
  11. voice?: string
  12. className?: string
  13. isAudition?: boolean
  14. }
  15. const AudioBtn = ({
  16. value,
  17. voice,
  18. className,
  19. isAudition,
  20. }: AudioBtnProps) => {
  21. const audioRef = useRef<HTMLAudioElement | null>(null)
  22. const [isPlaying, setIsPlaying] = useState(false)
  23. const [isPause, setPause] = useState(false)
  24. const [hasEnded, setHasEnded] = useState(false)
  25. const selector = useRef(`play-tooltip-${randomString(4)}`)
  26. const params = useParams()
  27. const pathname = usePathname()
  28. const removeCodeBlocks = (inputText: any) => {
  29. const codeBlockRegex = /```[\s\S]*?```/g
  30. if (inputText)
  31. return inputText.replace(codeBlockRegex, '')
  32. return ''
  33. }
  34. const playAudio = async () => {
  35. const formData = new FormData()
  36. if (value !== '') {
  37. formData.append('text', removeCodeBlocks(value))
  38. formData.append('voice', removeCodeBlocks(voice))
  39. let url = ''
  40. let isPublic = false
  41. if (params.token) {
  42. url = '/text-to-audio'
  43. isPublic = true
  44. }
  45. else if (params.appId) {
  46. if (pathname.search('explore/installed') > -1)
  47. url = `/installed-apps/${params.appId}/text-to-audio`
  48. else
  49. url = `/apps/${params.appId}/text-to-audio`
  50. }
  51. try {
  52. const audioResponse = await textToAudio(url, isPublic, formData)
  53. const blob_bytes = Buffer.from(audioResponse.data, 'latin1')
  54. const blob = new Blob([blob_bytes], { type: 'audio/wav' })
  55. const audioUrl = URL.createObjectURL(blob)
  56. const audio = new Audio(audioUrl)
  57. audioRef.current = audio
  58. audio.play().then(() => {}).catch(() => {
  59. setIsPlaying(false)
  60. URL.revokeObjectURL(audioUrl)
  61. })
  62. audio.onended = () => {
  63. setHasEnded(true)
  64. setIsPlaying(false)
  65. }
  66. }
  67. catch (error) {
  68. setIsPlaying(false)
  69. console.error('Error playing audio:', error)
  70. }
  71. }
  72. }
  73. const togglePlayPause = () => {
  74. if (audioRef.current) {
  75. if (isPlaying) {
  76. if (!hasEnded) {
  77. setPause(false)
  78. audioRef.current.play()
  79. }
  80. if (!isPause) {
  81. setPause(true)
  82. audioRef.current.pause()
  83. }
  84. }
  85. else if (!isPlaying) {
  86. if (isPause) {
  87. setPause(false)
  88. audioRef.current.play()
  89. }
  90. else {
  91. setHasEnded(false)
  92. playAudio().then()
  93. }
  94. }
  95. setIsPlaying(prevIsPlaying => !prevIsPlaying)
  96. }
  97. else {
  98. setIsPlaying(true)
  99. if (!isPlaying)
  100. playAudio().then()
  101. }
  102. }
  103. return (
  104. <div className={`${(isPlaying && !hasEnded) ? 'mr-1' : className}`}>
  105. <Tooltip
  106. selector={selector.current}
  107. content={(!isPause ? ((isPlaying && !hasEnded) ? t('appApi.playing') : t('appApi.play')) : t('appApi.pause')) as string}
  108. className='z-10'
  109. >
  110. <div
  111. className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || 'rounded-md bg-white'}`}
  112. style={{ boxShadow: !isAudition ? '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' : '' }}
  113. onClick={togglePlayPause}>
  114. <div className={`w-6 h-6 rounded-md ${!isAudition ? 'hover:bg-gray-200' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
  115. </div>
  116. </Tooltip>
  117. </div>
  118. )
  119. }
  120. export default AudioBtn