extract routes from main.py into domain routers (Phase 4)

Split the 2,578-line main.py (90 routes) into 12 domain-specific router
modules under routes/. main.py is now a 45-line entry point.

New structure:
- app.py: FastAPI instance, CORS, startup/shutdown, safe_content_disposition
- routes/: health, profiles, channels, generations, history, transcription,
  stories, effects, audio, models, tasks, cuda
- services/cuda.py: moved from cuda_download.py

Also includes Phase 5 database/ package (from parallel agent):
- database/__init__.py re-exports all symbols for backward compat
- database/models.py, session.py, migrations.py, seed.py

All 90 routes verified registered and app imports cleanly.
This commit is contained in:
James Pine
2026-03-16 02:03:15 -07:00
parent 89d6e364d4
commit 88536d27f7
23 changed files with 3088 additions and 3031 deletions
+155
View File
@@ -0,0 +1,155 @@
"""ORM model definitions for the voicebox SQLite database."""
from datetime import datetime
import uuid
from sqlalchemy import Column, String, Integer, Float, DateTime, Text, ForeignKey, Boolean
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class VoiceProfile(Base):
"""Voice profile."""
__tablename__ = "profiles"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, unique=True, nullable=False)
description = Column(Text)
language = Column(String, default="en")
avatar_path = Column(String, nullable=True)
effects_chain = Column(Text, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class ProfileSample(Base):
"""Audio sample attached to a voice profile."""
__tablename__ = "profile_samples"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
profile_id = Column(String, ForeignKey("profiles.id"), nullable=False)
audio_path = Column(String, nullable=False)
reference_text = Column(Text, nullable=False)
class Generation(Base):
"""A single TTS generation."""
__tablename__ = "generations"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
profile_id = Column(String, ForeignKey("profiles.id"), nullable=False)
text = Column(Text, nullable=False)
language = Column(String, default="en")
audio_path = Column(String, nullable=True)
duration = Column(Float, nullable=True)
seed = Column(Integer)
instruct = Column(Text)
engine = Column(String, default="qwen")
model_size = Column(String, nullable=True)
status = Column(String, default="completed")
error = Column(Text, nullable=True)
is_favorited = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
class Story(Base):
"""A story that sequences multiple generations."""
__tablename__ = "stories"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, nullable=False)
description = Column(Text)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class StoryItem(Base):
"""Links a generation to a story at a specific timecode."""
__tablename__ = "story_items"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
story_id = Column(String, ForeignKey("stories.id"), nullable=False)
generation_id = Column(String, ForeignKey("generations.id"), nullable=False)
version_id = Column(String, ForeignKey("generation_versions.id"), nullable=True)
start_time_ms = Column(Integer, nullable=False, default=0)
track = Column(Integer, nullable=False, default=0)
trim_start_ms = Column(Integer, nullable=False, default=0)
trim_end_ms = Column(Integer, nullable=False, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
class Project(Base):
"""Audio studio project (JSON blob)."""
__tablename__ = "projects"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, nullable=False)
data = Column(Text)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class GenerationVersion(Base):
"""A version of a generation's audio (original, processed, alternate takes)."""
__tablename__ = "generation_versions"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
generation_id = Column(String, ForeignKey("generations.id"), nullable=False)
label = Column(String, nullable=False)
audio_path = Column(String, nullable=False)
effects_chain = Column(Text, nullable=True)
source_version_id = Column(String, ForeignKey("generation_versions.id"), nullable=True)
is_default = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
class EffectPreset(Base):
"""Saved effect chain preset."""
__tablename__ = "effect_presets"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, unique=True, nullable=False)
description = Column(Text, nullable=True)
effects_chain = Column(Text, nullable=False)
is_builtin = Column(Boolean, default=False)
sort_order = Column(Integer, default=100)
created_at = Column(DateTime, default=datetime.utcnow)
class AudioChannel(Base):
"""Audio output channel (bus)."""
__tablename__ = "audio_channels"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, nullable=False)
is_default = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
class ChannelDeviceMapping(Base):
"""Mapping between a channel and an OS audio device."""
__tablename__ = "channel_device_mappings"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
channel_id = Column(String, ForeignKey("audio_channels.id"), nullable=False)
device_id = Column(String, nullable=False)
class ProfileChannelMapping(Base):
"""Many-to-many mapping between voice profiles and audio channels."""
__tablename__ = "profile_channel_mappings"
profile_id = Column(String, ForeignKey("profiles.id"), primary_key=True)
channel_id = Column(String, ForeignKey("audio_channels.id"), primary_key=True)