feat: Integrate IndexTTS2 model and update related schemas and frontend components

This commit is contained in:
2026-03-12 13:30:53 +08:00
parent e5b5a16364
commit 8aec4f6f44
151 changed files with 40077 additions and 85 deletions

View File

@@ -1,7 +1,7 @@
import { useState, useEffect, useCallback, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Book, Plus, Trash2, RefreshCw, Download, ChevronDown, ChevronUp, Play, Square, Pencil, Check, X, Loader2 } from 'lucide-react'
import { Book, Plus, Trash2, RefreshCw, Download, ChevronDown, ChevronUp, Play, Square, Pencil, Check, X, Loader2, Zap } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
@@ -337,7 +337,7 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
const [loadingAction, setLoadingAction] = useState(false)
const [isPolling, setIsPolling] = useState(false)
const [editingCharId, setEditingCharId] = useState<number | null>(null)
const [editFields, setEditFields] = useState({ name: '', gender: '', description: '', instruct: '' })
const [editFields, setEditFields] = useState({ name: '', gender: '', description: '', instruct: '', use_indextts2: false })
const [generatingChapterIndices, setGeneratingChapterIndices] = useState<Set<number>>(new Set())
const [sequentialPlayingId, setSequentialPlayingId] = useState<number | null>(null)
const [charsCollapsed, setCharsCollapsed] = useState(false)
@@ -635,7 +635,7 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
const startEditChar = (char: AudiobookCharacter) => {
setEditingCharId(char.id)
setEditFields({ name: char.name, gender: char.gender || '', description: char.description || '', instruct: char.instruct || '' })
setEditFields({ name: char.name, gender: char.gender || '', description: char.description || '', instruct: char.instruct || '', use_indextts2: char.use_indextts2 ?? false })
}
const saveEditChar = async (char: AudiobookCharacter) => {
@@ -645,6 +645,7 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
gender: editFields.gender || undefined,
description: editFields.description,
instruct: editFields.instruct,
use_indextts2: editFields.use_indextts2,
})
setEditingCharId(null)
await fetchDetail()
@@ -857,6 +858,16 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
onChange={e => setEditFields(f => ({ ...f, description: e.target.value }))}
placeholder={t('projectCard.characters.descPlaceholder')}
/>
<label className="flex items-center gap-2 text-sm cursor-pointer select-none">
<input
type="checkbox"
checked={editFields.use_indextts2}
onChange={e => setEditFields(f => ({ ...f, use_indextts2: e.target.checked }))}
className="w-4 h-4"
/>
<Zap className="h-3 w-3 text-amber-400" />
<span>使 IndexTTS2</span>
</label>
<div className="flex gap-2">
<Button size="sm" onClick={() => saveEditChar(char)}>
<Check className="h-3 w-3 mr-1" />{t('common:save')}
@@ -878,6 +889,11 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
</div>
<span className="text-xs text-muted-foreground truncate sm:mx-2 sm:flex-1">{char.instruct}</span>
<div className="flex items-center gap-1 shrink-0">
{char.use_indextts2 && (
<Badge variant="outline" className="text-xs border-amber-400/50 text-amber-400">
<Zap className="h-2.5 w-2.5 mr-0.5" />IndexTTS2
</Badge>
)}
{char.voice_design_id
? <Badge variant="outline" className="text-xs">{t('projectCard.characters.voiceDesign', { id: char.voice_design_id })}</Badge>
: <Badge variant="secondary" className="text-xs">{t('projectCard.characters.noVoice')}</Badge>