mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 00:55:14 -07:00
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:
+1
-1
@@ -100,7 +100,7 @@ function App() {
|
|||||||
<Sidebar activeTab={activeTab} onTabChange={setActiveTab} />
|
<Sidebar activeTab={activeTab} onTabChange={setActiveTab} />
|
||||||
|
|
||||||
<main className="flex-1 ml-20 overflow-hidden flex flex-col">
|
<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 />
|
<UpdateNotification />
|
||||||
|
|
||||||
{activeTab === 'settings' ? (
|
{activeTab === 'settings' ? (
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Plus, Trash2, Play } from 'lucide-react';
|
import { Plus, Trash2, Play } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
|
||||||
import { useDeleteSample, useProfileSamples } from '@/lib/hooks/useProfiles';
|
import { useDeleteSample, useProfileSamples } from '@/lib/hooks/useProfiles';
|
||||||
import { useServerStore } from '@/stores/serverStore';
|
|
||||||
import { usePlayerStore } from '@/stores/playerStore';
|
import { usePlayerStore } from '@/stores/playerStore';
|
||||||
|
import { apiClient } from '@/lib/api/client';
|
||||||
import { SampleUpload } from './SampleUpload';
|
import { SampleUpload } from './SampleUpload';
|
||||||
|
|
||||||
interface SampleListProps {
|
interface SampleListProps {
|
||||||
@@ -15,8 +14,6 @@ export function SampleList({ profileId }: SampleListProps) {
|
|||||||
const { data: samples, isLoading } = useProfileSamples(profileId);
|
const { data: samples, isLoading } = useProfileSamples(profileId);
|
||||||
const deleteSample = useDeleteSample();
|
const deleteSample = useDeleteSample();
|
||||||
const [uploadOpen, setUploadOpen] = useState(false);
|
const [uploadOpen, setUploadOpen] = useState(false);
|
||||||
const { toast } = useToast();
|
|
||||||
const serverUrl = useServerStore((state) => state.serverUrl);
|
|
||||||
const setAudio = usePlayerStore((state) => state.setAudio);
|
const setAudio = usePlayerStore((state) => state.setAudio);
|
||||||
const currentAudioId = usePlayerStore((state) => state.audioId);
|
const currentAudioId = usePlayerStore((state) => state.audioId);
|
||||||
const isPlaying = usePlayerStore((state) => state.isPlaying);
|
const isPlaying = usePlayerStore((state) => state.isPlaying);
|
||||||
@@ -27,8 +24,8 @@ export function SampleList({ profileId }: SampleListProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePlay = (audioPath: string, referenceText: string, sampleId: string) => {
|
const handlePlay = (referenceText: string, sampleId: string) => {
|
||||||
const audioUrl = `${serverUrl}${audioPath}`;
|
const audioUrl = apiClient.getSampleUrl(sampleId);
|
||||||
setAudio(audioUrl, sampleId, referenceText.substring(0, 50));
|
setAudio(audioUrl, sampleId, referenceText.substring(0, 50));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -65,7 +62,7 @@ export function SampleList({ profileId }: SampleListProps) {
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
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' : ''}
|
className={currentAudioId === sample.id && isPlaying ? 'text-primary' : ''}
|
||||||
>
|
>
|
||||||
<Play className="h-4 w-4 mr-1" />
|
<Play className="h-4 w-4 mr-1" />
|
||||||
|
|||||||
@@ -146,6 +146,10 @@ class ApiClient {
|
|||||||
return `${this.getBaseUrl()}/audio/${audioId}`;
|
return `${this.getBaseUrl()}/audio/${audioId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSampleUrl(sampleId: string): string {
|
||||||
|
return `${this.getBaseUrl()}/samples/${sampleId}`;
|
||||||
|
}
|
||||||
|
|
||||||
// Transcription
|
// Transcription
|
||||||
async transcribeAudio(file: File, language?: 'en' | 'zh'): Promise<TranscriptionResponse> {
|
async transcribeAudio(file: File, language?: 'en' | 'zh'): Promise<TranscriptionResponse> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|||||||
@@ -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
|
# MODEL MANAGEMENT
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user