"""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. voice_type discriminates three flavours: - "cloned" — traditional reference-audio profiles (all cloning engines) - "preset" — engine-specific pre-built voice (e.g. Kokoro voices) - "designed" — text-described voice (e.g. Qwen CustomVoice, future) """ __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) # Voice type system — added v0.3.x voice_type = Column(String, default="cloned") # "cloned" | "preset" | "designed" preset_engine = Column(String, nullable=True) # e.g. "kokoro" — only for preset preset_voice_id = Column(String, nullable=True) # e.g. "am_adam" — only for preset design_prompt = Column(Text, nullable=True) # text description — only for designed default_engine = Column(String, nullable=True) # auto-selected engine, locked for preset 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)