From dbfcff34762cd0a06f4fc6d30a470cce1680286c Mon Sep 17 00:00:00 2001 From: bdim404 Date: Fri, 13 Mar 2026 17:14:33 +0800 Subject: [PATCH] feat: enhance AudiobookCharacterResponse and AudioPlayer for compact mode support --- qwen3-tts-backend/api/audiobook.py | 49 ++++++++++--------- qwen3-tts-backend/schemas/audiobook.py | 2 + .../src/components/AudioPlayer.module.css | 8 +++ .../src/components/AudioPlayer.tsx | 22 +++++++-- qwen3-tts-frontend/src/lib/api/audiobook.ts | 2 + qwen3-tts-frontend/src/pages/Audiobook.tsx | 24 +++++---- 6 files changed, 70 insertions(+), 37 deletions(-) diff --git a/qwen3-tts-backend/api/audiobook.py b/qwen3-tts-backend/api/audiobook.py index 7551fb6..825089a 100644 --- a/qwen3-tts-backend/api/audiobook.py +++ b/qwen3-tts-backend/api/audiobook.py @@ -60,20 +60,32 @@ def _project_to_response(project, segment_total: int = 0, segment_done: int = 0) ) +def _char_to_response(c, db: Session) -> AudiobookCharacterResponse: + vd_name = None + vd_speaker = None + if c.voice_design_id: + from db.models import VoiceDesign + vd = db.query(VoiceDesign).filter(VoiceDesign.id == c.voice_design_id).first() + if vd: + vd_name = vd.name + meta = vd.meta_data or {} + vd_speaker = meta.get('speaker') or vd.aliyun_voice_id or vd.instruct or None + return AudiobookCharacterResponse( + id=c.id, + project_id=c.project_id, + name=c.name, + gender=c.gender, + description=c.description, + instruct=c.instruct, + voice_design_id=c.voice_design_id, + voice_design_name=vd_name, + voice_design_speaker=vd_speaker, + use_indextts2=c.use_indextts2 or False, + ) + + def _project_to_detail(project, db: Session) -> AudiobookProjectDetail: - characters = [ - AudiobookCharacterResponse( - id=c.id, - project_id=c.project_id, - name=c.name, - gender=c.gender, - description=c.description, - instruct=c.instruct, - voice_design_id=c.voice_design_id, - use_indextts2=c.use_indextts2 or False, - ) - for c in (project.characters or []) - ] + characters = [_char_to_response(c, db) for c in (project.characters or [])] chapters = [ AudiobookChapterResponse( id=ch.id, @@ -730,16 +742,7 @@ async def update_character( voice_design.instruct = data.instruct db.commit() - return AudiobookCharacterResponse( - id=char.id, - project_id=char.project_id, - name=char.name, - gender=char.gender, - description=char.description, - instruct=char.instruct, - voice_design_id=char.voice_design_id, - use_indextts2=char.use_indextts2 or False, - ) + return _char_to_response(char, db) @router.post("/projects/{project_id}/generate") diff --git a/qwen3-tts-backend/schemas/audiobook.py b/qwen3-tts-backend/schemas/audiobook.py index 14dbbaa..aeffa48 100644 --- a/qwen3-tts-backend/schemas/audiobook.py +++ b/qwen3-tts-backend/schemas/audiobook.py @@ -59,6 +59,8 @@ class AudiobookCharacterResponse(BaseModel): description: Optional[str] = None instruct: Optional[str] = None voice_design_id: Optional[int] = None + voice_design_name: Optional[str] = None + voice_design_speaker: Optional[str] = None use_indextts2: bool = False model_config = ConfigDict(from_attributes=True) diff --git a/qwen3-tts-frontend/src/components/AudioPlayer.module.css b/qwen3-tts-frontend/src/components/AudioPlayer.module.css index 8c49c2b..0bacc3e 100644 --- a/qwen3-tts-frontend/src/components/AudioPlayer.module.css +++ b/qwen3-tts-frontend/src/components/AudioPlayer.module.css @@ -70,3 +70,11 @@ min-height: 40px; min-width: 40px; } + +.compact { + padding: 0.25rem 0.5rem; +} + +.compact .downloadButton { + display: none; +} diff --git a/qwen3-tts-frontend/src/components/AudioPlayer.tsx b/qwen3-tts-frontend/src/components/AudioPlayer.tsx index ac35679..f3c4eeb 100644 --- a/qwen3-tts-frontend/src/components/AudioPlayer.tsx +++ b/qwen3-tts-frontend/src/components/AudioPlayer.tsx @@ -12,9 +12,10 @@ interface AudioPlayerProps { audioUrl: string jobId: number text?: string + compact?: boolean } -const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => { +const AudioPlayer = memo(({ audioUrl, jobId, compact }: AudioPlayerProps) => { const { t } = useTranslation('common') const { theme } = useTheme() const [blobUrl, setBlobUrl] = useState('') @@ -97,13 +98,13 @@ const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => { const player = new WaveformPlayer(containerRef.current, { url: blobUrl, waveformStyle: 'mirror', - height: 60, - barWidth: 3, + height: compact ? 32 : 60, + barWidth: compact ? 2 : 3, barSpacing: 1, - samples: 200, + samples: compact ? 80 : 200, waveformColor, progressColor, - showTime: true, + showTime: !compact, showPlaybackSpeed: false, autoplay: false, enableMediaSession: true, @@ -157,6 +158,17 @@ const AudioPlayer = memo(({ audioUrl, jobId }: AudioPlayerProps) => { return null } + if (compact) { + return ( +