Implement sample audio retrieval and update playback functionality

- Added a new API endpoint to serve profile sample audio files.
- Introduced a method in the apiClient to generate sample audio URLs.
- Refactored the SampleList component to utilize the new API for audio playback, enhancing the user experience.
- Cleaned up unused imports and optimized the handlePlay function for better performance.
This commit is contained in:
Jamie Pine
2026-01-25 18:44:25 -08:00
parent 6acad47335
commit c62f615162
5 changed files with 29 additions and 8 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ function App() {
<Sidebar activeTab={activeTab} onTabChange={setActiveTab} />
<main className="flex-1 ml-20 overflow-hidden flex flex-col">
<div className="container mx-auto px-8 py-8 max-w-[1800px] h-full overflow-hidden flex flex-col">
<div className="container mx-auto px-8 max-w-[1800px] h-full overflow-hidden flex flex-col">
<UpdateNotification />
{activeTab === 'settings' ? (
@@ -1,10 +1,9 @@
import { Plus, Trash2, Play } from 'lucide-react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { useToast } from '@/components/ui/use-toast';
import { useDeleteSample, useProfileSamples } from '@/lib/hooks/useProfiles';
import { useServerStore } from '@/stores/serverStore';
import { usePlayerStore } from '@/stores/playerStore';
import { apiClient } from '@/lib/api/client';
import { SampleUpload } from './SampleUpload';
interface SampleListProps {
@@ -15,8 +14,6 @@ export function SampleList({ profileId }: SampleListProps) {
const { data: samples, isLoading } = useProfileSamples(profileId);
const deleteSample = useDeleteSample();
const [uploadOpen, setUploadOpen] = useState(false);
const { toast } = useToast();
const serverUrl = useServerStore((state) => state.serverUrl);
const setAudio = usePlayerStore((state) => state.setAudio);
const currentAudioId = usePlayerStore((state) => state.audioId);
const isPlaying = usePlayerStore((state) => state.isPlaying);
@@ -27,8 +24,8 @@ export function SampleList({ profileId }: SampleListProps) {
}
};
const handlePlay = (audioPath: string, referenceText: string, sampleId: string) => {
const audioUrl = `${serverUrl}${audioPath}`;
const handlePlay = (referenceText: string, sampleId: string) => {
const audioUrl = apiClient.getSampleUrl(sampleId);
setAudio(audioUrl, sampleId, referenceText.substring(0, 50));
};
@@ -65,7 +62,7 @@ export function SampleList({ profileId }: SampleListProps) {
<Button
variant="ghost"
size="sm"
onClick={() => handlePlay(sample.audio_path, sample.reference_text, sample.id)}
onClick={() => handlePlay(sample.reference_text, sample.id)}
className={currentAudioId === sample.id && isPlaying ? 'text-primary' : ''}
>
<Play className="h-4 w-4 mr-1" />
+4
View File
@@ -146,6 +146,10 @@ class ApiClient {
return `${this.getBaseUrl()}/audio/${audioId}`;
}
getSampleUrl(sampleId: string): string {
return `${this.getBaseUrl()}/samples/${sampleId}`;
}
// Transcription
async transcribeAudio(file: File, language?: 'en' | 'zh'): Promise<TranscriptionResponse> {
const formData = new FormData();
+20
View File
@@ -427,6 +427,26 @@ async def get_audio(generation_id: str, db: Session = Depends(get_db)):
)
@app.get("/samples/{sample_id}")
async def get_sample_audio(sample_id: str, db: Session = Depends(get_db)):
"""Serve profile sample audio file."""
from .database import ProfileSample as DBProfileSample
sample = db.query(DBProfileSample).filter_by(id=sample_id).first()
if not sample:
raise HTTPException(status_code=404, detail="Sample not found")
audio_path = Path(sample.audio_path)
if not audio_path.exists():
raise HTTPException(status_code=404, detail="Audio file not found")
return FileResponse(
audio_path,
media_type="audio/wav",
filename=f"sample_{sample_id}.wav",
)
# ============================================
# MODEL MANAGEMENT
# ============================================
Binary file not shown.