mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 19:47:41 +08:00
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
from typing import List, Optional
|
|
|
|
from sqlalchemy import Index, String, delete, select
|
|
from sqlalchemy.orm import Mapped, Session, mapped_column
|
|
|
|
from app.db.base import Base, execute_dml, get_id_column
|
|
|
|
|
|
class TransferPending(Base):
|
|
"""
|
|
待整理文件登记。
|
|
|
|
整理队列是纯内存的 queue.Queue:进程一旦重启(挂载挂死后的人工重启、版本
|
|
升级、OOM、宿主重启),队列里的任务会连同「这些文件还没整理」这个事实一起
|
|
蒸发。而已经稳定落地的文件不会再产生任何监控事件,也不会有新的补偿扫描起点
|
|
——结果就是永久漏件,只能靠人工比对补整理。
|
|
|
|
这里只落盘最小事实:存储与源文件路径。重启后重新走一遍整理入口,由整理历史
|
|
查重挡掉已经完成的,因此不需要序列化 meta/mediainfo 这些重对象,也不存在
|
|
识别结果陈旧的问题。
|
|
"""
|
|
|
|
id = get_id_column()
|
|
# 存储
|
|
storage: Mapped[str] = mapped_column(String, nullable=False)
|
|
# 源文件路径
|
|
src_path: Mapped[str] = mapped_column(String, nullable=False)
|
|
# 登记时间
|
|
created_at: Mapped[Optional[str]] = mapped_column(String)
|
|
|
|
__table_args__ = (
|
|
# 同一个文件重复入队只保留一条,回放时不会重复送入整理链
|
|
Index("ux_transferpending_storage_path", "storage", "src_path", unique=True),
|
|
)
|
|
|
|
@classmethod
|
|
def register(cls, db: Session, storage: str, src_path: str,
|
|
now_time: str) -> Optional["TransferPending"]:
|
|
"""
|
|
登记一个待整理文件,已存在时保持原登记时间不变。
|
|
:param db: 数据库会话
|
|
:param storage: 存储
|
|
:param src_path: 源文件路径
|
|
:param now_time: 当前时间
|
|
:return: 登记记录
|
|
"""
|
|
if not storage or not src_path:
|
|
return None
|
|
pending = db.execute(
|
|
select(cls).where(cls.storage == storage, cls.src_path == src_path)
|
|
).scalars().first()
|
|
if pending:
|
|
return pending
|
|
pending = cls(storage=storage, src_path=src_path, created_at=now_time)
|
|
db.add(pending)
|
|
return pending
|
|
|
|
@classmethod
|
|
def discard(cls, db: Session, storage: str, src_path: str) -> int:
|
|
"""
|
|
注销一个待整理文件登记,整理到达终态(成功或失败)时调用。
|
|
:param db: 数据库会话
|
|
:param storage: 存储
|
|
:param src_path: 源文件路径
|
|
:return: 删除的记录数
|
|
"""
|
|
if not storage or not src_path:
|
|
return 0
|
|
return execute_dml(
|
|
db, delete(cls).where(cls.storage == storage, cls.src_path == src_path),
|
|
execution_options={"synchronize_session": False},
|
|
)
|
|
|
|
@classmethod
|
|
def list_all(cls, db: Session, limit: Optional[int] = 5000) -> List["TransferPending"]:
|
|
"""
|
|
列出全部待整理登记,供启动回放使用。
|
|
|
|
按登记时间升序回放,保持与原入队顺序一致;上限避免异常积压时
|
|
一次性把整理链压垮。
|
|
:param db: 数据库会话
|
|
:param limit: 单次回放上限
|
|
:return: 待整理登记列表
|
|
"""
|
|
return list(db.execute(
|
|
select(cls)
|
|
.order_by(cls.created_at.asc(), cls.id.asc())
|
|
.limit(limit)
|
|
).scalars().all())
|
|
|
|
@classmethod
|
|
def clear(cls, db: Session) -> int:
|
|
"""
|
|
清空全部待整理登记。
|
|
:param db: 数据库会话
|
|
:return: 删除的记录数
|
|
"""
|
|
return execute_dml(
|
|
db, delete(cls),
|
|
execution_options={"synchronize_session": False},
|
|
)
|