Files
you-music/backend/app/models/models.py
T
2025-11-07 20:42:44 +11:00

155 lines
6.6 KiB
Python

from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, Table, Text, Boolean
from sqlalchemy.orm import relationship
from datetime import datetime, timedelta
from app.db.session import Base
import secrets
# Association table for playlist-music many-to-many relationship
playlist_music = Table(
'playlist_music',
Base.metadata,
Column('playlist_id', Integer, ForeignKey('playlists.id'), primary_key=True),
Column('music_id', Integer, ForeignKey('music.id'), primary_key=True),
Column('position', Integer, default=0),
Column('added_at', DateTime, default=datetime.utcnow),
)
class Music(Base):
__tablename__ = "music"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
artist = Column(String, index=True, nullable=True)
album = Column(String, nullable=True)
duration = Column(Float, nullable=True)
file_path = Column(String, unique=True)
file_size = Column(Integer, nullable=True)
file_format = Column(String, nullable=True) # mp3, flac, m4a, wav, ogg, etc.
file_location = Column(String, nullable=True) # Full absolute path for user reference
file_exists = Column(Boolean, default=True) # Track if file still exists on disk
source_url = Column(String, nullable=True)
source_type = Column(String, nullable=True) # local, youtube, bilibili, etc.
thumbnail = Column(String, nullable=True)
lyrics = Column(Text, nullable=True)
share_token = Column(String, unique=True, index=True, nullable=True) # Secure share token
share_token_expires_at = Column(DateTime, nullable=True) # Expiration timestamp
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
last_scanned_at = Column(DateTime, nullable=True) # Last time file was verified
playlists = relationship("Playlist", secondary=playlist_music, back_populates="music_items")
@property
def artist_list(self) -> list:
"""Get list of individual artists from the artist field"""
if not self.artist or self.artist == "Unknown":
return ["Unknown"]
import re
# Split by / or , and clean up whitespace
artists = re.split(r'[/,]', self.artist)
return [a.strip() for a in artists if a.strip()]
@property
def display_artist(self) -> str:
"""Get display-friendly artist string"""
artists = self.artist_list
if not artists or artists == ["Unknown"]:
return "Unknown"
return ", ".join(artists)
def generate_share_token(self, expiration_days: int = 14):
"""Generate a secure random share token with expiration"""
if not self.share_token:
self.share_token = secrets.token_urlsafe(16)
self.share_token_expires_at = datetime.utcnow() + timedelta(days=expiration_days)
def is_share_token_valid(self) -> bool:
"""Check if share token is still valid"""
if not self.share_token or not self.share_token_expires_at:
return False
return datetime.utcnow() < self.share_token_expires_at
class Playlist(Base):
__tablename__ = "playlists"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True)
description = Column(Text, nullable=True)
thumbnail = Column(String, nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
music_items = relationship("Music", secondary=playlist_music, back_populates="playlists")
class AppSettings(Base):
__tablename__ = "app_settings"
id = Column(Integer, primary_key=True, index=True)
key = Column(String, unique=True, index=True)
value = Column(Text)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class DownloadJob(Base):
__tablename__ = "download_jobs"
id = Column(Integer, primary_key=True, index=True)
song_name = Column(String, index=True)
status = Column(String, index=True) # pending, searching, downloading, completed, failed, waiting_confirmation
search_results = Column(Text, nullable=True) # JSON string of search results
selected_result = Column(Text, nullable=True) # JSON string of selected result
selected_result_index = Column(Integer, nullable=True) # Index of selected result
priority = Column(Boolean, default=False) # True if contains "official" or "官方"
error_message = Column(Text, nullable=True)
music_id = Column(Integer, ForeignKey('music.id'), nullable=True) # Reference to downloaded music
confirmed = Column(Boolean, default=False) # Whether user confirmed duplicate download
is_duplicate = Column(Boolean, default=False) # Whether song already exists
duplicate_music_id = Column(Integer, nullable=True) # ID of existing duplicate
thumbnail = Column(String, nullable=True) # Thumbnail URL from search results
created_at = Column(DateTime, default=datetime.utcnow, index=True)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class APIKey(Base):
__tablename__ = "api_keys"
id = Column(Integer, primary_key=True, index=True)
key = Column(String, unique=True, index=True)
name = Column(String)
description = Column(Text, nullable=True)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
expires_at = Column(DateTime, nullable=True)
last_used_at = Column(DateTime, nullable=True)
class ScanHistory(Base):
__tablename__ = "scan_history"
id = Column(Integer, primary_key=True, index=True)
started_at = Column(DateTime, default=datetime.utcnow, index=True)
completed_at = Column(DateTime, nullable=True)
files_scanned = Column(Integer, default=0)
files_added = Column(Integer, default=0)
files_updated = Column(Integer, default=0)
files_missing = Column(Integer, default=0)
errors_count = Column(Integer, default=0)
status = Column(String, default="in_progress") # in_progress, completed, failed
created_at = Column(DateTime, default=datetime.utcnow)
class ArtistAlias(Base):
"""Artist alias management for merging artists with different names"""
__tablename__ = "artist_aliases"
id = Column(Integer, primary_key=True, index=True)
display_name = Column(String, index=True) # The main name to display
alias_name = Column(String, index=True, unique=True) # An alias (could be English/Chinese name, etc.)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)