from datetime import datetime from app.extensions import db class Resource(db.Model): __tablename__ = 'resources' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, index=True) title = db.Column(db.String(255), nullable=False) description = db.Column(db.Text, nullable=True) # 资源类型: text / image / audio / video resource_type = db.Column(db.Enum('text', 'image', 'audio', 'video'), nullable=False, index=True) # 来源类型: upload / url / magnet source_type = db.Column(db.Enum('upload', 'url', 'magnet'), nullable=False, default='upload') # 文件信息 filename = db.Column(db.String(255), nullable=True) # 磁盘上的文件名 original_name = db.Column(db.String(255), nullable=True) # 原始文件名 file_path = db.Column(db.String(512), nullable=True) # 相对于 static 的路径 file_size = db.Column(db.BigInteger, nullable=True) # 字节 mime_type = db.Column(db.String(128), nullable=True) # 来源 URL / 磁力链 source_url = db.Column(db.Text, nullable=True) # 下载状态: pending / downloading / done / failed (针对 url/magnet) download_status = db.Column( db.Enum('pending', 'downloading', 'done', 'failed', 'na'), nullable=False, default='na' ) download_progress = db.Column(db.Integer, default=0) # 0-100 download_error = db.Column(db.Text, nullable=True) tags = db.Column(db.String(512), nullable=True) # 逗号分隔 is_public = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) def __repr__(self): return f'' @property def size_human(self): """返回人性化的文件大小字符串""" if not self.file_size: return '未知' for unit in ['B', 'KB', 'MB', 'GB', 'TB']: if self.file_size < 1024: return f'{self.file_size:.1f} {unit}' self.file_size /= 1024 return f'{self.file_size:.1f} PB' @property def tag_list(self): if not self.tags: return [] return [t.strip() for t in self.tags.split(',') if t.strip()] @property def type_icon(self): icons = { 'text': 'bi-file-text', 'image': 'bi-image', 'audio': 'bi-music-note-beamed', 'video': 'bi-camera-video', } return icons.get(self.resource_type, 'bi-file') @property def type_badge_color(self): colors = { 'text': 'secondary', 'image': 'success', 'audio': 'warning', 'video': 'danger', } return colors.get(self.resource_type, 'secondary')