models.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. from sqlalchemy import Column, String, Float, DateTime, Integer, Text, Boolean, ForeignKey
  2. from sqlalchemy.ext.declarative import declarative_base
  3. from sqlalchemy.orm import relationship
  4. from sqlalchemy.sql import func
  5. from datetime import datetime
  6. Base = declarative_base()
  7. class User(Base):
  8. """User account with Audiobookshelf credentials."""
  9. __tablename__ = "users"
  10. id = Column(Integer, primary_key=True, autoincrement=True)
  11. username = Column(String, unique=True, nullable=False, index=True)
  12. email = Column(String, unique=True, nullable=False, index=True)
  13. hashed_password = Column(String, nullable=False)
  14. # Per-user Audiobookshelf credentials
  15. abs_url = Column(String, nullable=False)
  16. abs_api_token = Column(String, nullable=False) # Encrypted with Fernet
  17. # Profile information
  18. display_name = Column(String)
  19. created_at = Column(DateTime, default=func.now())
  20. last_login = Column(DateTime)
  21. is_active = Column(Boolean, default=True)
  22. # Relationships
  23. listening_sessions = relationship("ListeningSession", back_populates="user", cascade="all, delete-orphan")
  24. recommendations = relationship("Recommendation", back_populates="user", cascade="all, delete-orphan")
  25. class Book(Base):
  26. """Book information from Audiobookshelf."""
  27. __tablename__ = "books"
  28. id = Column(String, primary_key=True) # Audiobookshelf book ID
  29. title = Column(String, nullable=False)
  30. author = Column(String)
  31. narrator = Column(String)
  32. description = Column(Text)
  33. genres = Column(String) # JSON string of genres
  34. tags = Column(String) # JSON string of tags
  35. duration = Column(Float) # Duration in seconds
  36. cover_url = Column(String)
  37. created_at = Column(DateTime, default=func.now())
  38. updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
  39. class ListeningSession(Base):
  40. """User listening sessions and progress."""
  41. __tablename__ = "listening_sessions"
  42. id = Column(Integer, primary_key=True, autoincrement=True)
  43. user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
  44. book_id = Column(String, nullable=False)
  45. # Progress tracking
  46. progress = Column(Float, default=0.0) # 0.0 to 1.0
  47. current_time = Column(Float, default=0.0) # Current position in seconds
  48. is_finished = Column(Boolean, default=False)
  49. # Timestamps
  50. started_at = Column(DateTime)
  51. finished_at = Column(DateTime, nullable=True)
  52. last_update = Column(DateTime, default=func.now(), onupdate=func.now())
  53. # Ratings and preferences
  54. rating = Column(Integer, nullable=True) # 1-5 stars, optional
  55. # Relationships
  56. user = relationship("User", back_populates="listening_sessions")
  57. class Recommendation(Base):
  58. """AI-generated book recommendations."""
  59. __tablename__ = "recommendations"
  60. id = Column(Integer, primary_key=True, autoincrement=True)
  61. user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
  62. # Recommendation details
  63. title = Column(String, nullable=False)
  64. author = Column(String)
  65. description = Column(Text)
  66. reason = Column(Text) # Why this book was recommended
  67. # Metadata
  68. genres = Column(String) # JSON string
  69. created_at = Column(DateTime, default=func.now())
  70. dismissed = Column(Boolean, default=False)
  71. # Relationships
  72. user = relationship("User", back_populates="recommendations")