/*
* @Author: ZhaoYing
* @Date: 2026-03-16 15:00:07
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:00:07
*/
import { type FC, useRef, useState, useEffect } from 'react'
import { Flex, Dropdown, type MenuProps, Slider } from 'antd'
import clsx from 'clsx'
import { useTranslation } from 'react-i18next'
/** Available playback speed options. */
const SPEEDS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
/** Format seconds into "MM:SS" display string. */
const fmt = (s: number) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(Math.floor(s % 60)).padStart(2, '0')}`
/**
* Props for the AudioPlayer component.
* @property src - Audio file URL to play.
* @property fileName - Display name shown beside the file icon.
* @property fileSize - Human-readable file size string (e.g. "3.2 MB").
*/
interface AudioPlayerProps {
src: string
fileName: string
fileSize: string
}
/**
* AudioPlayer – A compact inline audio player with playback controls.
*
* Displays file metadata (name & size), a play/pause toggle, a seekable
* progress slider, elapsed/total time, and a dropdown menu for downloading
* the file or changing playback speed.
*
* @example
*
*/
const AudioPlayer: FC = ({ src, fileName, fileSize }) => {
const { t } = useTranslation()
const audioRef = useRef(null)
const [playing, setPlaying] = useState(false)
const [current, setCurrent] = useState(0)
const [duration, setDuration] = useState(0)
const [speed, setSpeed] = useState(1)
/* Bind native audio events to sync React state; re-binds when src changes. */
useEffect(() => {
const audio = audioRef.current
if (!audio) return
const onTime = () => setCurrent(audio.currentTime)
const onMeta = () => setDuration(audio.duration)
const onEnd = () => setPlaying(false)
audio.addEventListener('timeupdate', onTime)
audio.addEventListener('loadedmetadata', onMeta)
audio.addEventListener('ended', onEnd)
return () => {
audio.removeEventListener('timeupdate', onTime)
audio.removeEventListener('loadedmetadata', onMeta)
audio.removeEventListener('ended', onEnd)
}
}, [src])
/** Toggle between play and pause. */
const togglePlay = () => {
const audio = audioRef.current
if (!audio) return
if (playing) { audio.pause(); setPlaying(false) }
else { audio.play(); setPlaying(true) }
}
/** Seek to a specific position (in seconds) on the audio timeline. */
const handleSeek = (val: number) => {
if (audioRef.current) audioRef.current.currentTime = val
setCurrent(val)
}
/** Update playback speed on both React state and the native audio element. */
const setPlaybackSpeed = (s: number) => {
setSpeed(s)
if (audioRef.current) audioRef.current.playbackRate = s
}
/** Open the audio source URL in a new tab to trigger download. */
const handleDownload = () => window.open(src, '_blank')
/** Dropdown menu items: download and playback speed sub-menu. */
const mainMenu: MenuProps = {
items: [
{
key: 'download',
icon: ,
label: t('common.download'),
onClick: handleDownload,
},
{
key: 'speed',
icon: ,
label: t('perceptualDetail.playbackSpeed'),
children: SPEEDS.map(s => ({
key: String(s),
label: {s === 1 ? 'normal' : s},
onClick: () => setPlaybackSpeed(s),
})),
},
],
}
return (
{fileName}
{fileSize || '-'}
{fmt(current)} / {fmt(duration)}
)
}
export default AudioPlayer