diff --git a/qwen3-tts-backend/core/audiobook_service.py b/qwen3-tts-backend/core/audiobook_service.py
index 013e568..ce09250 100644
--- a/qwen3-tts-backend/core/audiobook_service.py
+++ b/qwen3-tts-backend/core/audiobook_service.py
@@ -398,9 +398,12 @@ async def parse_one_chapter(project_id: int, chapter_id: int, user: User, db) ->
char = char_map.get(seg.get("character", "narrator")) or char_map.get("narrator")
if not char:
continue
+ seg_emo_text = seg.get("emo_text", "") or None
+ seg_emo_alpha = seg.get("emo_alpha") if seg_emo_text else None
crud.create_audiobook_segment(
db, project_id, char.id, seg_text,
chapter.chapter_index, seg_counter,
+ emo_text=seg_emo_text, emo_alpha=seg_emo_alpha,
)
seg_counter += 1
chunk_count += 1
@@ -580,8 +583,8 @@ async def generate_project(project_id: int, user: User, db: Session, chapter_ind
text=seg.text,
spk_audio_prompt=design.ref_audio_path,
output_path=str(audio_path),
- emo_text=char.instruct or None,
- emo_alpha=0.6,
+ emo_text=seg.emo_text or None,
+ emo_alpha=seg.emo_alpha if seg.emo_text else 0.5,
)
else:
if design.voice_cache_id:
diff --git a/qwen3-tts-backend/core/llm_service.py b/qwen3-tts-backend/core/llm_service.py
index 9908104..b93613b 100644
--- a/qwen3-tts-backend/core/llm_service.py
+++ b/qwen3-tts-backend/core/llm_service.py
@@ -201,9 +201,14 @@ class LLMService:
system_prompt = (
"你是一个专业的有声书制作助手。请将给定的章节文本解析为对话片段列表。"
f"已知角色列表(必须从中选择):{names_str}。"
- "所有非对话的叙述文字归属于narrator角色。"
+ "所有非对话的叙述文字归属于narrator角色。\n"
+ "同时根据语境为每个片段判断情绪,可选情绪及对应强度如下(必须严格使用以下值):\n"
+ "开心(emo_alpha=0.6)、愤怒(emo_alpha=0.15)、悲伤(emo_alpha=0.4)、恐惧(emo_alpha=0.4)、"
+ "厌恶(emo_alpha=0.6)、低沉(emo_alpha=0.6)、惊讶(emo_alpha=0.3)、中性(emo_alpha=0.5)。\n"
+ "narrator旁白及情绪不明显的片段,emo_text设为\"\",emo_alpha设为0.5。\n"
"只输出JSON数组,不要有其他文字,格式如下:\n"
- '[{"character": "narrator", "text": "叙述文字"}, {"character": "角色名", "text": "对话内容"}, ...]'
+ '[{"character": "narrator", "text": "叙述文字", "emo_text": "", "emo_alpha": 0.5}, '
+ '{"character": "角色名", "text": "对话内容", "emo_text": "开心", "emo_alpha": 0.6}, ...]'
)
user_message = f"请解析以下章节文本:\n\n{chapter_text}"
result = await self.stream_chat_json(system_prompt, user_message, on_token, max_tokens=16384)
diff --git a/qwen3-tts-backend/db/crud.py b/qwen3-tts-backend/db/crud.py
index 1d0b220..789f2e3 100644
--- a/qwen3-tts-backend/db/crud.py
+++ b/qwen3-tts-backend/db/crud.py
@@ -593,6 +593,8 @@ def create_audiobook_segment(
text: str,
chapter_index: int = 0,
segment_index: int = 0,
+ emo_text: Optional[str] = None,
+ emo_alpha: Optional[float] = None,
) -> AudiobookSegment:
seg = AudiobookSegment(
project_id=project_id,
@@ -600,6 +602,8 @@ def create_audiobook_segment(
text=text,
chapter_index=chapter_index,
segment_index=segment_index,
+ emo_text=emo_text or None,
+ emo_alpha=emo_alpha,
status="pending",
)
db.add(seg)
diff --git a/qwen3-tts-backend/db/database.py b/qwen3-tts-backend/db/database.py
index f81b2eb..2d87988 100644
--- a/qwen3-tts-backend/db/database.py
+++ b/qwen3-tts-backend/db/database.py
@@ -40,3 +40,12 @@ def init_db():
conn.commit()
except Exception:
pass
+ for col_def in [
+ "ALTER TABLE audiobook_segments ADD COLUMN emo_text VARCHAR(20)",
+ "ALTER TABLE audiobook_segments ADD COLUMN emo_alpha REAL",
+ ]:
+ try:
+ conn.execute(__import__("sqlalchemy").text(col_def))
+ conn.commit()
+ except Exception:
+ pass
diff --git a/qwen3-tts-backend/db/models.py b/qwen3-tts-backend/db/models.py
index 59b2667..b456c52 100644
--- a/qwen3-tts-backend/db/models.py
+++ b/qwen3-tts-backend/db/models.py
@@ -1,6 +1,6 @@
from datetime import datetime
from enum import Enum
-from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, Index, JSON
+from sqlalchemy import Column, Integer, String, Boolean, DateTime, Float, ForeignKey, Text, Index, JSON
from sqlalchemy.orm import relationship
from db.database import Base
@@ -189,6 +189,8 @@ class AudiobookSegment(Base):
segment_index = Column(Integer, nullable=False)
character_id = Column(Integer, ForeignKey("audiobook_characters.id"), nullable=False)
text = Column(Text, nullable=False)
+ emo_text = Column(String(20), nullable=True)
+ emo_alpha = Column(Float, nullable=True)
audio_path = Column(String(500), nullable=True)
status = Column(String(20), default="pending", nullable=False)
diff --git a/qwen3-tts-backend/schemas/audiobook.py b/qwen3-tts-backend/schemas/audiobook.py
index 936f9cd..c2ee3f3 100644
--- a/qwen3-tts-backend/schemas/audiobook.py
+++ b/qwen3-tts-backend/schemas/audiobook.py
@@ -81,6 +81,8 @@ class AudiobookSegmentResponse(BaseModel):
character_id: int
character_name: Optional[str] = None
text: str
+ emo_text: Optional[str] = None
+ emo_alpha: Optional[float] = None
audio_path: Optional[str] = None
status: str
diff --git a/qwen3-tts-frontend/src/lib/api/audiobook.ts b/qwen3-tts-frontend/src/lib/api/audiobook.ts
index 155e6e6..99d4ec8 100644
--- a/qwen3-tts-frontend/src/lib/api/audiobook.ts
+++ b/qwen3-tts-frontend/src/lib/api/audiobook.ts
@@ -45,6 +45,8 @@ export interface AudiobookSegment {
character_id: number
character_name?: string
text: string
+ emo_text?: string
+ emo_alpha?: number
audio_path?: string
status: string
}
diff --git a/qwen3-tts-frontend/src/pages/Audiobook.tsx b/qwen3-tts-frontend/src/pages/Audiobook.tsx
index 5acdae1..a45537d 100644
--- a/qwen3-tts-frontend/src/pages/Audiobook.tsx
+++ b/qwen3-tts-frontend/src/pages/Audiobook.tsx
@@ -1082,6 +1082,11 @@ function ProjectCard({ project, onRefresh }: { project: AudiobookProject; onRefr
{seg.character_name || t('projectCard.segments.unknownCharacter')}
+ {seg.emo_text && (
+
+ {seg.emo_text}
+
+ )}
{seg.status === 'generating' && }
{seg.status === 'error' && {t('projectCard.segments.errorBadge')}}