feat(audiobook): implement chapter management with CRUD operations and enhance project detail responses

This commit is contained in:
2026-03-10 16:42:32 +08:00
parent 01b6f4633e
commit 3c30afc476
8 changed files with 393 additions and 156 deletions

View File

@@ -3,7 +3,7 @@ from typing import Optional, List, Dict, Any
from datetime import datetime
from sqlalchemy.orm import Session
from db.models import User, Job, VoiceCache, SystemSettings, VoiceDesign, AudiobookProject, AudiobookCharacter, AudiobookSegment
from db.models import User, Job, VoiceCache, SystemSettings, VoiceDesign, AudiobookProject, AudiobookChapter, AudiobookCharacter, AudiobookSegment
def get_user_by_username(db: Session, username: str) -> Optional[User]:
return db.query(User).filter(User.username == username).first()
@@ -449,6 +449,66 @@ def delete_audiobook_project(db: Session, project_id: int, user_id: int) -> bool
return True
def create_audiobook_chapter(
db: Session,
project_id: int,
chapter_index: int,
source_text: str,
title: Optional[str] = None,
) -> AudiobookChapter:
chapter = AudiobookChapter(
project_id=project_id,
chapter_index=chapter_index,
source_text=source_text,
title=title,
status="pending",
)
db.add(chapter)
db.commit()
db.refresh(chapter)
return chapter
def get_audiobook_chapter(db: Session, chapter_id: int) -> Optional[AudiobookChapter]:
return db.query(AudiobookChapter).filter(AudiobookChapter.id == chapter_id).first()
def list_audiobook_chapters(db: Session, project_id: int) -> List[AudiobookChapter]:
return db.query(AudiobookChapter).filter(
AudiobookChapter.project_id == project_id
).order_by(AudiobookChapter.chapter_index).all()
def update_audiobook_chapter_status(
db: Session,
chapter_id: int,
status: str,
error_message: Optional[str] = None,
) -> Optional[AudiobookChapter]:
chapter = db.query(AudiobookChapter).filter(AudiobookChapter.id == chapter_id).first()
if not chapter:
return None
chapter.status = status
if error_message is not None:
chapter.error_message = error_message
db.commit()
db.refresh(chapter)
return chapter
def delete_audiobook_chapters(db: Session, project_id: int) -> None:
db.query(AudiobookChapter).filter(AudiobookChapter.project_id == project_id).delete()
db.commit()
def delete_audiobook_segments_for_chapter(db: Session, project_id: int, chapter_index: int) -> None:
db.query(AudiobookSegment).filter(
AudiobookSegment.project_id == project_id,
AudiobookSegment.chapter_index == chapter_index,
).delete()
db.commit()
def create_audiobook_character(
db: Session,
project_id: int,

View File

@@ -141,9 +141,28 @@ class AudiobookProject(Base):
user = relationship("User", back_populates="audiobook_projects")
characters = relationship("AudiobookCharacter", back_populates="project", cascade="all, delete-orphan")
chapters = relationship("AudiobookChapter", back_populates="project", cascade="all, delete-orphan", order_by="AudiobookChapter.chapter_index")
segments = relationship("AudiobookSegment", back_populates="project", cascade="all, delete-orphan")
class AudiobookChapter(Base):
__tablename__ = "audiobook_chapters"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey("audiobook_projects.id"), nullable=False, index=True)
chapter_index = Column(Integer, nullable=False)
title = Column(String(500), nullable=True)
source_text = Column(Text, nullable=False)
status = Column(String(20), default="pending", nullable=False)
error_message = Column(Text, nullable=True)
project = relationship("AudiobookProject", back_populates="chapters")
__table_args__ = (
Index('idx_chapter_project_idx', 'project_id', 'chapter_index'),
)
class AudiobookCharacter(Base):
__tablename__ = "audiobook_characters"