feat: Update dependencies and add new voice

- Updates project dependencies to latest versions for improved performance and security.
- Adds a new voice option, 'Aoede', to the available voices.
- Modifies the voice change handler to force re-generation of future audio segments for the selected article.
- Updates Vite configuration to align with newer Vite versions and load environment variables correctly.
- Adjusts TypeScript configuration for a more modern setup.
- Removes unnecessary configuration from Nginx file.
This commit is contained in:
Anthony
2025-11-19 20:46:02 +08:00
parent 0b10d71554
commit 43435166f8
9 changed files with 80 additions and 55 deletions

38
App.tsx
View File

@@ -91,6 +91,8 @@ export default function App() {
const segmentsToBuffer = article.segments.slice(currentIndex, currentIndex + 5);
for (const seg of segmentsToBuffer) {
// Only process if we don't have audio URL, and it's not currently loading.
// This handles cases where we cleared the URL to force regeneration.
if (!seg.audioUrl && !seg.isLoading && !seg.hasError) {
processSegmentAudio(article.id, seg.id, seg.text, playerState.selectedVoice);
}
@@ -99,6 +101,38 @@ export default function App() {
// -- Handlers --
const handleVoiceChange = useCallback((newVoice: VoiceName) => {
setPlayerState(prev => ({ ...prev, selectedVoice: newVoice }));
// Force flush future buffer so new voice is applied immediately
setQueue(prevQueue => prevQueue.map(article => {
// If this is the currently active article
if (article.id === playerState.currentArticleId) {
return {
...article,
segments: article.segments.map((seg, idx) => {
// Keep the current segment (and past ones) to avoid cutting off mid-speech abruptly
if (idx <= article.currentSegmentIndex) {
return seg;
}
// Invalidate all future segments
return { ...seg, audioUrl: undefined, isLoading: false, hasError: false };
})
};
}
// For inactive articles, invalidate everything
return {
...article,
segments: article.segments.map(seg => ({
...seg,
audioUrl: undefined,
isLoading: false,
hasError: false
}))
};
}));
}, [playerState.currentArticleId]);
const handleAddUrl = async () => {
if (!inputUrl.trim()) return;
@@ -373,8 +407,8 @@ export default function App() {
<div className="flex items-center gap-4">
<VoiceSelector
selectedVoice={playerState.selectedVoice}
onVoiceChange={(v) => setPlayerState(prev => ({ ...prev, selectedVoice: v }))}
disabled={playerState.isPlaying}
onVoiceChange={handleVoiceChange}
// Removed disabled prop to allow switching while playing
/>
<button
onClick={() => setShowSettings(!showSettings)}