mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-02 22:17:08 +08:00
refactor: isolate transfer and download history queries
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
SQLAlchemy 归还连接时已在池层吞掉异常并 invalidate 坏连接,再把释放故障升级成调用方
|
||||
的异常,只会让一次已经落库的写入看起来像失败,诱发重复提交。
|
||||
"""
|
||||
from functools import wraps
|
||||
from inspect import Parameter, signature
|
||||
from typing import Any, Awaitable, Callable, Optional, TypeVar
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -296,3 +298,79 @@ def async_db_query(func: Callable[..., Awaitable[_R]]) -> Callable[..., Awaitabl
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def legacy_db_query(func: Callable[..., _R]) -> Callable[..., _R]:
|
||||
"""保留旧 Model 查询 ABI,同时让新调用方复用显式 Session。
|
||||
|
||||
旧插件通常省略 ``db``,直接把业务参数放在第一个位置;通用 ``db_query``
|
||||
装饰器只适用于固定的 ``(db, ...)`` 形状,不能把这类位置参数直接套进去。
|
||||
这里按签名插入会话,避免丢失旧插件传入的第一个业务参数。
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> _R:
|
||||
db = _get_args_db(args, kwargs)
|
||||
if db is not None:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
session = ScopedSession()
|
||||
call_args, call_kwargs = _inject_legacy_db(func, args, kwargs, session)
|
||||
try:
|
||||
return func(*call_args, **call_kwargs)
|
||||
finally:
|
||||
try:
|
||||
session.close()
|
||||
except Exception as close_err: # noqa: BLE001 释放故障不得改变旧 ABI 返回值
|
||||
logger.error(f"释放数据库会话失败:{close_err}")
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def legacy_async_db_query(
|
||||
func: Callable[..., Awaitable[_R]],
|
||||
) -> Callable[..., Awaitable[_R]]:
|
||||
"""保留旧 Model 异步查询 ABI,同时让新调用方复用显式 AsyncSession。"""
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(*args: Any, **kwargs: Any) -> _R:
|
||||
db = _get_args_async_db(args, kwargs)
|
||||
if db is not None:
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
async with async_session_scope() as session:
|
||||
call_args, call_kwargs = _inject_legacy_db(func, args, kwargs, session)
|
||||
return await func(*call_args, **call_kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def _inject_legacy_db(
|
||||
func: Callable[..., _R],
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
db: Any,
|
||||
) -> tuple[tuple[Any, ...], dict[str, Any]]:
|
||||
"""按旧 Model 方法签名注入兼容会话,不吞掉位置业务参数。"""
|
||||
call_args = list(args)
|
||||
call_kwargs = dict(kwargs)
|
||||
parameters = list(signature(func).parameters.values())
|
||||
db_index = next(
|
||||
(index for index, parameter in enumerate(parameters) if parameter.name == "db"),
|
||||
None,
|
||||
)
|
||||
if "db" in call_kwargs:
|
||||
call_kwargs["db"] = db
|
||||
return tuple(call_args), call_kwargs
|
||||
if db_index is None:
|
||||
# 兼容没有显式 db 参数的极旧函数,保持调用失败方式与普通 Python 一致。
|
||||
return tuple(call_args), {"db": db, **call_kwargs}
|
||||
if db_index < len(call_args) and call_args[db_index] is None:
|
||||
call_args[db_index] = db
|
||||
elif db_index < len(parameters) and parameters[db_index].kind is Parameter.POSITIONAL_ONLY:
|
||||
call_args.insert(db_index, db)
|
||||
elif db_index <= len(call_args):
|
||||
call_args.insert(db_index, db)
|
||||
else:
|
||||
call_kwargs["db"] = db
|
||||
return tuple(call_args), call_kwargs
|
||||
|
||||
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Mapped, Session, mapped_column
|
||||
|
||||
from app.db.base import Base, execute_dml, get_id_column
|
||||
from app.db.decorators import async_db_query, db_query
|
||||
from app.db.decorators import legacy_async_db_query, legacy_db_query
|
||||
from app.db.models._constraints import media_identity_constraint
|
||||
from app.schemas.types import MediaSource
|
||||
|
||||
@@ -77,7 +77,7 @@ class DownloadHistory(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_hash(cls, db: Session, download_hash: str):
|
||||
return db.execute(
|
||||
select(DownloadHistory)
|
||||
@@ -86,7 +86,7 @@ class DownloadHistory(Base):
|
||||
).scalars().first()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_hashes(cls, db: Session, download_hashes: List[str]):
|
||||
"""
|
||||
批量查询多个下载任务的最新历史记录,避免在上层形成 N+1 查询。
|
||||
@@ -119,7 +119,7 @@ class DownloadHistory(Base):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_media_identity(
|
||||
cls, db: Session, media_source: MediaSource, media_id: str,
|
||||
music_type: Optional[str] = None,
|
||||
@@ -136,7 +136,7 @@ class DownloadHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_page(
|
||||
cls, db: Session, page: int = 1, count: int = 30
|
||||
):
|
||||
@@ -148,7 +148,7 @@ class DownloadHistory(Base):
|
||||
).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_list_by_page(
|
||||
cls, db: AsyncSession, page: int = 1, count: int = 30
|
||||
):
|
||||
@@ -161,7 +161,7 @@ class DownloadHistory(Base):
|
||||
return list(result.scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_list_by_title(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
@@ -177,13 +177,13 @@ class DownloadHistory(Base):
|
||||
return list(result.scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_count(cls, db: AsyncSession):
|
||||
result = await db.execute(select(func.count(cls.id)))
|
||||
return result.scalar()
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_count_by_title(cls, db: AsyncSession, title: str):
|
||||
result = await db.execute(
|
||||
select(func.count(cls.id)).filter(_title_like(cls.title, title))
|
||||
@@ -191,14 +191,14 @@ class DownloadHistory(Base):
|
||||
return result.scalar()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_path(cls, db: Session, path: str):
|
||||
return db.execute(
|
||||
select(DownloadHistory).where(DownloadHistory.path == path)
|
||||
).scalars().first()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_last_by(
|
||||
cls,
|
||||
db: Session,
|
||||
@@ -237,7 +237,7 @@ class DownloadHistory(Base):
|
||||
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_user_date(cls, db: Session, date: str, username: Optional[str] = None):
|
||||
"""
|
||||
查询某用户某时间之前的下载历史。
|
||||
@@ -256,7 +256,7 @@ class DownloadHistory(Base):
|
||||
).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_date(
|
||||
cls,
|
||||
db: Session,
|
||||
@@ -282,7 +282,7 @@ class DownloadHistory(Base):
|
||||
).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_type(cls, db: Session, mtype: str, days: int):
|
||||
return list(db.execute(
|
||||
select(DownloadHistory).where(
|
||||
@@ -345,7 +345,7 @@ class DownloadFiles(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_hash(cls, db: Session, download_hash: str, state: Optional[int] = None):
|
||||
statement = select(cls).where(cls.download_hash == download_hash)
|
||||
if state is not None:
|
||||
@@ -353,7 +353,7 @@ class DownloadFiles(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_fullpath(cls, db: Session, fullpath: str, all_files: bool = False):
|
||||
result = db.execute(
|
||||
select(cls).where(cls.fullpath == fullpath).order_by(cls.id.desc())
|
||||
@@ -361,7 +361,7 @@ class DownloadFiles(Base):
|
||||
return list(result.all()) if all_files else result.first()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_savepath(cls, db: Session, savepath: str):
|
||||
return list(db.execute(select(cls).where(cls.savepath == savepath)).scalars().all())
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Mapped, Session, mapped_column
|
||||
|
||||
from app.db.base import Base, execute_dml, get_id_column
|
||||
from app.db.decorators import async_db_query, db_query
|
||||
from app.db.decorators import (
|
||||
legacy_async_db_query,
|
||||
legacy_db_query,
|
||||
run_legacy_sync_query,
|
||||
)
|
||||
from app.db.models._constraints import media_identity_constraint
|
||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, MUSIC_ENTITY_RECORDING, MediaSource, MediaType
|
||||
|
||||
@@ -94,7 +98,7 @@ class TransferHistory(Base):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_title(cls, db: Session, title: str, page: int = 1, count: int = 30,
|
||||
status: Optional[bool] = None, wildcard: bool = False):
|
||||
if wildcard:
|
||||
@@ -121,7 +125,7 @@ class TransferHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_list_by_title(cls, db: AsyncSession, title: str, page: int = 1, count: int = 30,
|
||||
status: Optional[bool] = None, wildcard: bool = False):
|
||||
if wildcard:
|
||||
@@ -149,7 +153,7 @@ class TransferHistory(Base):
|
||||
return list(result.scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_page(cls, db: Session, page: int = 1, count: int = 30, status: Optional[bool] = None):
|
||||
statement = select(cls)
|
||||
if status is not None:
|
||||
@@ -163,7 +167,7 @@ class TransferHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_list_by_page(cls, db: AsyncSession, page: int = 1, count: int = 30,
|
||||
status: Optional[bool] = None):
|
||||
if status is not None:
|
||||
@@ -185,16 +189,29 @@ class TransferHistory(Base):
|
||||
return list(result.scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_hash(cls, db: Session, download_hash: str):
|
||||
return db.execute(
|
||||
select(cls).where(cls.download_hash == download_hash)
|
||||
).scalars().first()
|
||||
def get_by_hash(
|
||||
cls,
|
||||
db: Session | str | None = None,
|
||||
download_hash: str | None = None,
|
||||
):
|
||||
"""按下载哈希查询最新记录,兼容旧插件无会话调用。"""
|
||||
if download_hash is None and isinstance(db, str):
|
||||
download_hash, db = db, None
|
||||
if download_hash is None:
|
||||
raise TypeError("download_hash is required")
|
||||
|
||||
def query(session: Session):
|
||||
"""在调用方提供的同步会话中执行哈希查询。"""
|
||||
return session.execute(
|
||||
select(cls).where(cls.download_hash == download_hash)
|
||||
).scalars().first()
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_src(
|
||||
cls, db: Session, src: str, storage: Optional[str] = None
|
||||
cls, db: Session | str | None = None, src: str | None = None,
|
||||
storage: Optional[str] = None
|
||||
) -> Optional["TransferHistory"]:
|
||||
"""
|
||||
按源路径和存储查询单条整理记录。
|
||||
@@ -204,17 +221,26 @@ class TransferHistory(Base):
|
||||
:param storage: 源存储类型
|
||||
:return: 命中的整理记录,未命中时返回 None
|
||||
"""
|
||||
statement = select(cls).where(cls.src == src)
|
||||
if storage:
|
||||
statement = statement.where(cls.src_storage == storage)
|
||||
return db.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
if src is None and isinstance(db, str):
|
||||
src, db = db, None
|
||||
if src is None:
|
||||
raise TypeError("src is required")
|
||||
|
||||
def query(session: Session):
|
||||
"""在调用方提供的同步会话中执行源路径查询。"""
|
||||
statement = select(cls).where(cls.src == src)
|
||||
if storage:
|
||||
statement = statement.where(cls.src_storage == storage)
|
||||
return session.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_success_by_src(
|
||||
cls, db: Session, src: str, storage: Optional[str] = None
|
||||
cls, db: Session | str | None = None, src: str | None = None,
|
||||
storage: Optional[str] = None
|
||||
) -> Optional["TransferHistory"]:
|
||||
"""
|
||||
按源路径和存储查询成功的整理记录,源路径原样精确匹配。
|
||||
@@ -226,17 +252,26 @@ class TransferHistory(Base):
|
||||
:param storage: 源存储类型
|
||||
:return: 命中的成功整理记录,未命中时返回 None
|
||||
"""
|
||||
statement = select(cls).where(cls.src == src, cls.status.is_(True))
|
||||
if storage:
|
||||
statement = statement.where(cls.src_storage == storage)
|
||||
return db.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
if src is None and isinstance(db, str):
|
||||
src, db = db, None
|
||||
if src is None:
|
||||
raise TypeError("src is required")
|
||||
|
||||
def query(session: Session):
|
||||
"""在调用方提供的同步会话中执行成功源路径查询。"""
|
||||
statement = select(cls).where(cls.src == src, cls.status.is_(True))
|
||||
if storage:
|
||||
statement = statement.where(cls.src_storage == storage)
|
||||
return session.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_dest(
|
||||
cls, db: Session, dest: str, storage: Optional[str] = None
|
||||
cls, db: Session | str | None = None, dest: str | None = None,
|
||||
storage: Optional[str] = None
|
||||
) -> Optional["TransferHistory"]:
|
||||
"""
|
||||
按目标路径和存储查询单条整理记录。
|
||||
@@ -246,15 +281,24 @@ class TransferHistory(Base):
|
||||
:param storage: 目标存储类型
|
||||
:return: 命中的整理记录,未命中时返回 None
|
||||
"""
|
||||
statement = select(cls).where(cls.dest == dest)
|
||||
if storage:
|
||||
statement = statement.where(cls.dest_storage == storage)
|
||||
return db.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
if dest is None and isinstance(db, str):
|
||||
dest, db = db, None
|
||||
if dest is None:
|
||||
raise TypeError("dest is required")
|
||||
|
||||
def query(session: Session):
|
||||
"""在调用方提供的同步会话中执行目标路径查询。"""
|
||||
statement = select(cls).where(cls.dest == dest)
|
||||
if storage:
|
||||
statement = statement.where(cls.dest_storage == storage)
|
||||
return session.execute(
|
||||
statement.order_by(cls.id.desc())
|
||||
).scalars().first()
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_success_by_src(
|
||||
cls,
|
||||
db: Session,
|
||||
@@ -294,7 +338,7 @@ class TransferHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_success_move_by_dest(
|
||||
cls,
|
||||
db: Session,
|
||||
@@ -337,14 +381,14 @@ class TransferHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_hash(cls, db: Session, download_hash: str):
|
||||
return list(db.execute(
|
||||
select(cls).where(cls.download_hash == download_hash)
|
||||
).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def statistic(cls, db: Session, days: int = 7):
|
||||
"""
|
||||
统计最近days天的下载历史数量,按日期分组返回每日数量
|
||||
@@ -361,7 +405,7 @@ class TransferHistory(Base):
|
||||
).all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def monthly_media_statistics(cls, db: Session):
|
||||
"""
|
||||
统计当月成功整理的电影、电视剧、剧集和音乐数量。
|
||||
@@ -427,7 +471,7 @@ class TransferHistory(Base):
|
||||
return 1
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_statistic(cls, db: AsyncSession, days: int = 7):
|
||||
"""
|
||||
统计最近days天的下载历史数量,按日期分组返回每日数量
|
||||
@@ -442,7 +486,7 @@ class TransferHistory(Base):
|
||||
return result.all()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def count(cls, db: Session, status: Optional[bool] = None):
|
||||
statement = select(func.count(cls.id))
|
||||
if status is not None:
|
||||
@@ -450,7 +494,7 @@ class TransferHistory(Base):
|
||||
return db.execute(statement).scalar()
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_count(cls, db: AsyncSession, status: Optional[bool] = None):
|
||||
if status is not None:
|
||||
result = await db.execute(
|
||||
@@ -463,7 +507,7 @@ class TransferHistory(Base):
|
||||
return result.scalar()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def count_by_title(cls, db: Session, title: str, status: Optional[bool] = None, wildcard: bool = False):
|
||||
if wildcard:
|
||||
text_filter = or_(
|
||||
@@ -483,7 +527,7 @@ class TransferHistory(Base):
|
||||
return db.execute(statement).scalar()
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
@legacy_async_db_query
|
||||
async def async_count_by_title(cls, db: AsyncSession, title: str, status: Optional[bool] = None, wildcard: bool = False):
|
||||
if wildcard:
|
||||
text_filter = or_(
|
||||
@@ -504,7 +548,7 @@ class TransferHistory(Base):
|
||||
return result.scalar()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by(cls, db: Session, mtype: Optional[str] = None, title: Optional[str] = None, year: Optional[str] = None,
|
||||
season: Optional[str] = None,
|
||||
episode: Optional[str] = None,
|
||||
@@ -542,7 +586,7 @@ class TransferHistory(Base):
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def get_by_media_identity(
|
||||
cls, db: Session, media_source: MediaSource, media_id: str,
|
||||
mtype: Optional[str] = None,
|
||||
@@ -589,7 +633,7 @@ class TransferHistory(Base):
|
||||
return history
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
@legacy_db_query
|
||||
def list_by_date(cls, db: Session, date: str):
|
||||
"""
|
||||
查询某时间之后的转移历史
|
||||
|
||||
@@ -18,20 +18,28 @@ class DownloadHistoryOper(DbOper):
|
||||
按路径查询下载记录
|
||||
:param path: 数据key
|
||||
"""
|
||||
return DownloadHistory.get_by_path(self._db, path)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.get_by_path(session, path)
|
||||
)
|
||||
|
||||
def get_by_hash(self, download_hash: str) -> Optional[DownloadHistory]:
|
||||
"""
|
||||
按Hash查询下载记录
|
||||
:param download_hash: 数据key
|
||||
"""
|
||||
return DownloadHistory.get_by_hash(self._db, download_hash)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.get_by_hash(session, download_hash)
|
||||
)
|
||||
|
||||
def get_by_hashes(self, download_hashes: List[str]) -> Dict[str, DownloadHistory]:
|
||||
"""
|
||||
批量按 Hash 查询下载记录,并返回以 Hash 为键的映射。
|
||||
"""
|
||||
histories = DownloadHistory.get_by_hashes(self._db, download_hashes)
|
||||
histories = self._execute_sync_query(
|
||||
lambda session: DownloadHistory.get_by_hashes(
|
||||
session, download_hashes
|
||||
)
|
||||
)
|
||||
return {
|
||||
history.download_hash: history
|
||||
for history in histories
|
||||
@@ -48,11 +56,13 @@ class DownloadHistoryOper(DbOper):
|
||||
:param media_id: 数据源原生 ID
|
||||
:param music_type: 音乐实体类型
|
||||
"""
|
||||
return DownloadHistory.get_by_media_identity(
|
||||
self._db,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
music_type=music_type,
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.get_by_media_identity(
|
||||
session,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
music_type=music_type,
|
||||
)
|
||||
)
|
||||
|
||||
def add(self, **kwargs):
|
||||
@@ -97,30 +107,48 @@ class DownloadHistoryOper(DbOper):
|
||||
:param download_hash: 数据key
|
||||
:param state: 删除状态
|
||||
"""
|
||||
return DownloadFiles.get_by_hash(self._db, download_hash, state)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadFiles.get_by_hash(
|
||||
session, download_hash, state
|
||||
)
|
||||
)
|
||||
|
||||
def get_file_by_fullpath(self, fullpath: str) -> Optional[DownloadFiles]:
|
||||
"""
|
||||
按fullpath查询下载文件记录
|
||||
:param fullpath: 数据key
|
||||
"""
|
||||
return cast(Optional[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=False))
|
||||
return self._execute_sync_query(
|
||||
lambda session: cast(
|
||||
Optional[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(
|
||||
session, fullpath=fullpath, all_files=False
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def get_files_by_fullpath(self, fullpath: str) -> List[DownloadFiles]:
|
||||
"""
|
||||
按fullpath查询下载文件记录
|
||||
:param fullpath: 数据key
|
||||
"""
|
||||
return cast(List[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=True))
|
||||
return self._execute_sync_query(
|
||||
lambda session: cast(
|
||||
List[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(
|
||||
session, fullpath=fullpath, all_files=True
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def get_files_by_savepath(self, fullpath: str) -> List[DownloadFiles]:
|
||||
"""
|
||||
按savepath查询下载文件记录
|
||||
:param fullpath: 数据key
|
||||
"""
|
||||
return DownloadFiles.get_by_savepath(self._db, fullpath)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadFiles.get_by_savepath(session, fullpath)
|
||||
)
|
||||
|
||||
def delete_file_by_fullpath(self, fullpath: str):
|
||||
"""
|
||||
@@ -147,8 +175,14 @@ class DownloadHistoryOper(DbOper):
|
||||
按fullpath查询下载文件记录hash
|
||||
:param fullpath: 数据key
|
||||
"""
|
||||
fileinfo = cast(Optional[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(self._db, fullpath=fullpath, all_files=False))
|
||||
fileinfo = self._execute_sync_query(
|
||||
lambda session: cast(
|
||||
Optional[DownloadFiles],
|
||||
DownloadFiles.get_by_fullpath(
|
||||
session, fullpath=fullpath, all_files=False
|
||||
),
|
||||
)
|
||||
)
|
||||
if fileinfo:
|
||||
return fileinfo.download_hash
|
||||
return ""
|
||||
@@ -157,7 +191,9 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
分页查询下载历史
|
||||
"""
|
||||
return DownloadHistory.list_by_page(self._db, page, count)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.list_by_page(session, page, count)
|
||||
)
|
||||
|
||||
async def async_list_by_page(
|
||||
self,
|
||||
@@ -165,7 +201,11 @@ class DownloadHistoryOper(DbOper):
|
||||
count: int = 30,
|
||||
) -> List[DownloadHistory]:
|
||||
"""异步分页查询下载历史。"""
|
||||
return await DownloadHistory.async_list_by_page(self._db, page, count)
|
||||
return await self._execute_async_query(
|
||||
lambda session: DownloadHistory.async_list_by_page(
|
||||
session, page, count
|
||||
)
|
||||
)
|
||||
|
||||
async def async_delete_history(self, historyid: int):
|
||||
"""
|
||||
@@ -187,22 +227,30 @@ class DownloadHistoryOper(DbOper):
|
||||
按类型、标题、年份、季集查询下载记录
|
||||
媒体身份 + mtype 或 title + year
|
||||
"""
|
||||
return DownloadHistory.get_last_by(db=self._db,
|
||||
mtype=mtype,
|
||||
title=title,
|
||||
year=year,
|
||||
season=season,
|
||||
episode=episode,
|
||||
media_source=media_source,
|
||||
media_id=media_id)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.get_last_by(
|
||||
db=session,
|
||||
mtype=mtype,
|
||||
title=title,
|
||||
year=year,
|
||||
season=season,
|
||||
episode=episode,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
)
|
||||
)
|
||||
|
||||
def list_by_user_date(self, date: str, username: Optional[str] = None) -> List[DownloadHistory]:
|
||||
"""
|
||||
查询某用户某时间之前的下载历史
|
||||
"""
|
||||
return DownloadHistory.list_by_user_date(db=self._db,
|
||||
date=date,
|
||||
username=username)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.list_by_user_date(
|
||||
db=session,
|
||||
date=date,
|
||||
username=username,
|
||||
)
|
||||
)
|
||||
|
||||
def list_by_date(
|
||||
self, date: str, type: str, media_source: MediaSource, media_id: str,
|
||||
@@ -211,20 +259,28 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
查询某时间之后的下载历史
|
||||
"""
|
||||
return DownloadHistory.list_by_date(db=self._db,
|
||||
date=date,
|
||||
type=type,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
seasons=seasons)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.list_by_date(
|
||||
db=session,
|
||||
date=date,
|
||||
type=type,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
seasons=seasons,
|
||||
)
|
||||
)
|
||||
|
||||
def list_by_type(self, mtype: str, days: int = 7) -> List[DownloadHistory]:
|
||||
"""
|
||||
获取指定类型的下载历史
|
||||
"""
|
||||
return DownloadHistory.list_by_type(db=self._db,
|
||||
mtype=mtype,
|
||||
days=days)
|
||||
return self._execute_sync_query(
|
||||
lambda session: DownloadHistory.list_by_type(
|
||||
db=session,
|
||||
mtype=mtype,
|
||||
days=days,
|
||||
)
|
||||
)
|
||||
|
||||
def delete_history(self, historyid):
|
||||
"""
|
||||
|
||||
@@ -19,13 +19,17 @@ class TransferHistoryOper(DbOper):
|
||||
获取转移历史
|
||||
:param historyid: 转移历史id
|
||||
"""
|
||||
return TransferHistory.get(self._db, historyid)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.get(session, historyid)
|
||||
)
|
||||
|
||||
async def async_get(self, historyid: int) -> Optional[TransferHistory]:
|
||||
"""
|
||||
异步获取转移历史。
|
||||
"""
|
||||
return await TransferHistory.async_get(self._db, historyid)
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_get(session, historyid)
|
||||
)
|
||||
|
||||
async def async_list_by_title(
|
||||
self,
|
||||
@@ -38,13 +42,15 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
异步按标题分页查询转移记录。
|
||||
"""
|
||||
return await TransferHistory.async_list_by_title(
|
||||
self._db,
|
||||
title=title,
|
||||
page=page,
|
||||
count=count,
|
||||
status=status,
|
||||
wildcard=wildcard,
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_list_by_title(
|
||||
session,
|
||||
title=title,
|
||||
page=page,
|
||||
count=count,
|
||||
status=status,
|
||||
wildcard=wildcard,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_list_by_page(
|
||||
@@ -56,15 +62,19 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
异步分页查询转移记录。
|
||||
"""
|
||||
return await TransferHistory.async_list_by_page(
|
||||
self._db, page=page, count=count, status=status
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_list_by_page(
|
||||
session, page=page, count=count, status=status
|
||||
)
|
||||
)
|
||||
|
||||
async def async_count(self, status: Optional[bool] = None) -> Optional[int]:
|
||||
"""
|
||||
异步统计转移记录数量。
|
||||
"""
|
||||
return await TransferHistory.async_count(self._db, status=status)
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_count(session, status=status)
|
||||
)
|
||||
|
||||
async def async_count_by_title(
|
||||
self,
|
||||
@@ -75,11 +85,13 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
异步按标题统计转移记录数量。
|
||||
"""
|
||||
return await TransferHistory.async_count_by_title(
|
||||
self._db,
|
||||
title=title,
|
||||
status=status,
|
||||
wildcard=wildcard,
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_count_by_title(
|
||||
session,
|
||||
title=title,
|
||||
status=status,
|
||||
wildcard=wildcard,
|
||||
)
|
||||
)
|
||||
|
||||
def get_by_title(self, title: str) -> List[TransferHistory]:
|
||||
@@ -87,7 +99,9 @@ class TransferHistoryOper(DbOper):
|
||||
按标题查询转移记录
|
||||
:param title: 数据key
|
||||
"""
|
||||
return TransferHistory.list_by_title(self._db, title)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_by_title(session, title)
|
||||
)
|
||||
|
||||
def get_by_src(
|
||||
self, src: str, storage: Optional[str] = None
|
||||
@@ -98,7 +112,9 @@ class TransferHistoryOper(DbOper):
|
||||
:param storage: 存储类型
|
||||
:return: 命中的整理记录,未命中时返回 None
|
||||
"""
|
||||
return TransferHistory.get_by_src(self._db, src, storage)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.get_by_src(session, src, storage)
|
||||
)
|
||||
|
||||
def get_success_by_src(
|
||||
self, src: str, storage: Optional[str] = None
|
||||
@@ -109,7 +125,11 @@ class TransferHistoryOper(DbOper):
|
||||
:param storage: 存储类型
|
||||
:return: 命中的成功整理记录,未命中时返回 None
|
||||
"""
|
||||
return TransferHistory.get_success_by_src(self._db, src, storage)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.get_success_by_src(
|
||||
session, src, storage
|
||||
)
|
||||
)
|
||||
|
||||
def get_by_dest(
|
||||
self, dest: str, storage: Optional[str] = None
|
||||
@@ -119,7 +139,9 @@ class TransferHistoryOper(DbOper):
|
||||
:param dest: 数据key
|
||||
:param storage: 存储类型
|
||||
"""
|
||||
return TransferHistory.get_by_dest(self._db, dest, storage)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.get_by_dest(session, dest, storage)
|
||||
)
|
||||
|
||||
def list_success_by_src(
|
||||
self,
|
||||
@@ -135,11 +157,13 @@ class TransferHistoryOper(DbOper):
|
||||
:param recursive: 是否递归匹配目录子项
|
||||
:return: 命中的成功整理记录
|
||||
"""
|
||||
return TransferHistory.list_success_by_src(
|
||||
self._db,
|
||||
src=src,
|
||||
storage=storage,
|
||||
recursive=recursive,
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_success_by_src(
|
||||
session,
|
||||
src=src,
|
||||
storage=storage,
|
||||
recursive=recursive,
|
||||
)
|
||||
)
|
||||
|
||||
def list_success_move_by_dest(
|
||||
@@ -156,11 +180,13 @@ class TransferHistoryOper(DbOper):
|
||||
:param recursive: 是否递归匹配目录子项
|
||||
:return: 命中的成功移动记录
|
||||
"""
|
||||
return TransferHistory.list_success_move_by_dest(
|
||||
self._db,
|
||||
dest=dest,
|
||||
storage=storage,
|
||||
recursive=recursive,
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_success_move_by_dest(
|
||||
session,
|
||||
dest=dest,
|
||||
storage=storage,
|
||||
recursive=recursive,
|
||||
)
|
||||
)
|
||||
|
||||
def list_by_hash(self, download_hash: str) -> List[TransferHistory]:
|
||||
@@ -168,7 +194,9 @@ class TransferHistoryOper(DbOper):
|
||||
按种子hash查询转移记录
|
||||
:param download_hash: 种子hash
|
||||
"""
|
||||
return TransferHistory.list_by_hash(self._db, download_hash)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_by_hash(session, download_hash)
|
||||
)
|
||||
|
||||
def add(self, **kwargs):
|
||||
"""
|
||||
@@ -183,15 +211,21 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
统计最近days天的下载历史数量
|
||||
"""
|
||||
return TransferHistory.statistic(self._db, days)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.statistic(session, days)
|
||||
)
|
||||
|
||||
async def async_statistic(self, days: int = 7) -> List[Any]:
|
||||
"""异步统计最近若干天的整理历史数量。"""
|
||||
return await TransferHistory.async_statistic(self._db, days)
|
||||
return await self._execute_async_query(
|
||||
lambda session: TransferHistory.async_statistic(session, days)
|
||||
)
|
||||
|
||||
def monthly_media_statistics(self) -> tuple[int, int, int, int]:
|
||||
"""统计本月成功整理的电影、剧集、单集和音乐数量。"""
|
||||
return TransferHistory.monthly_media_statistics(self._db)
|
||||
return self._execute_sync_query(
|
||||
TransferHistory.monthly_media_statistics
|
||||
)
|
||||
|
||||
def get_by(self, title: Optional[str] = None, year: Optional[str] = None, mtype: Optional[str] = None,
|
||||
season: Optional[str] = None, episode: Optional[str] = None,
|
||||
@@ -200,26 +234,32 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
按类型、标题、年份、季集查询转移记录
|
||||
"""
|
||||
return TransferHistory.list_by(db=self._db,
|
||||
mtype=mtype,
|
||||
title=title,
|
||||
dest=dest,
|
||||
year=year,
|
||||
season=season,
|
||||
episode=episode,
|
||||
media_source=media_source,
|
||||
media_id=media_id)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_by(
|
||||
db=session,
|
||||
mtype=mtype,
|
||||
title=title,
|
||||
dest=dest,
|
||||
year=year,
|
||||
season=season,
|
||||
episode=episode,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
)
|
||||
)
|
||||
|
||||
def get_by_media_identity(
|
||||
self, media_source: MediaSource, media_id: str,
|
||||
mtype: Optional[str] = None,
|
||||
) -> Optional[TransferHistory]:
|
||||
"""按规范媒体身份和类型查询整理记录。"""
|
||||
return TransferHistory.get_by_media_identity(
|
||||
db=self._db,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
mtype=mtype,
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.get_by_media_identity(
|
||||
db=session,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
mtype=mtype,
|
||||
)
|
||||
)
|
||||
|
||||
def delete(self, historyid):
|
||||
@@ -312,4 +352,6 @@ class TransferHistoryOper(DbOper):
|
||||
查询某时间之后的转移历史
|
||||
:param date: 日期
|
||||
"""
|
||||
return TransferHistory.list_by_date(self._db, date)
|
||||
return self._execute_sync_query(
|
||||
lambda session: TransferHistory.list_by_date(session, date)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user