新增agent删除下载历史记录工具

This commit is contained in:
jxxghp
2026-03-27 11:50:46 +08:00
parent d7c62fb55a
commit a25c709927
3 changed files with 239 additions and 68 deletions

View File

@@ -36,6 +36,7 @@ from app.agent.tools.impl.query_workflows import QueryWorkflowsTool
from app.agent.tools.impl.run_workflow import RunWorkflowTool
from app.agent.tools.impl.update_site_cookie import UpdateSiteCookieTool
from app.agent.tools.impl.delete_download import DeleteDownloadTool
from app.agent.tools.impl.delete_download_history import DeleteDownloadHistoryTool
from app.agent.tools.impl.modify_download import ModifyDownloadTool
from app.agent.tools.impl.query_directory_settings import QueryDirectorySettingsTool
from app.agent.tools.impl.list_directory import ListDirectoryTool
@@ -95,6 +96,7 @@ class MoviePilotToolFactory:
DeleteSubscribeTool,
QueryDownloadTasksTool,
DeleteDownloadTool,
DeleteDownloadHistoryTool,
ModifyDownloadTool,
QueryDownloadersTool,
QuerySitesTool,

View File

@@ -0,0 +1,43 @@
"""删除下载历史记录工具"""
from typing import Optional, Type
from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.db import AsyncSessionFactory
from app.db.models.downloadhistory import DownloadHistory
from app.log import logger
class DeleteDownloadHistoryInput(BaseModel):
"""删除下载历史记录工具的输入参数模型"""
explanation: str = Field(
...,
description="Clear explanation of why this tool is being used in the current context",
)
history_id: int = Field(
..., description="The ID of the download history record to delete"
)
class DeleteDownloadHistoryTool(MoviePilotTool):
name: str = "delete_download_history"
description: str = "Delete a download history record by ID. This only removes the record from the database, does not delete any actual files."
args_schema: Type[BaseModel] = DeleteDownloadHistoryInput
def get_tool_message(self, **kwargs) -> Optional[str]:
history_id = kwargs.get("history_id")
return f"正在删除下载历史记录 ID: {history_id}"
async def run(self, history_id: int, **kwargs) -> str:
logger.info(f"执行工具: {self.name}, 参数: history_id={history_id}")
try:
async with AsyncSessionFactory() as db:
await DownloadHistory.async_delete(db, history_id)
return f"下载历史记录 ID: {history_id} 已成功删除"
except Exception as e:
logger.error(f"删除下载历史记录失败: {e}", exc_info=True)
return f"删除下载历史记录时发生错误: {str(e)}"

View File

@@ -12,6 +12,7 @@ class DownloadHistory(Base):
"""
下载历史记录
"""
id = get_id_column()
# 保存路径
path = Column(String, nullable=False, index=True)
@@ -61,32 +62,73 @@ class DownloadHistory(Base):
@classmethod
@db_query
def get_by_hash(cls, db: Session, download_hash: str):
return db.query(DownloadHistory).filter(DownloadHistory.download_hash == download_hash).order_by(
DownloadHistory.date.desc()
).first()
return (
db.query(DownloadHistory)
.filter(DownloadHistory.download_hash == download_hash)
.order_by(DownloadHistory.date.desc())
.first()
)
@classmethod
@db_query
def get_by_mediaid(cls, db: Session, tmdbid: int, doubanid: str):
if tmdbid:
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid).all()
return (
db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid).all()
)
elif doubanid:
return db.query(DownloadHistory).filter(DownloadHistory.doubanid == doubanid).all()
return (
db.query(DownloadHistory)
.filter(DownloadHistory.doubanid == doubanid)
.all()
)
return []
@classmethod
@db_query
def list_by_page(cls, db: Session, page: Optional[int] = 1, count: Optional[int] = 30):
def list_by_page(
cls, db: Session, page: Optional[int] = 1, count: Optional[int] = 30
):
return db.query(DownloadHistory).offset((page - 1) * count).limit(count).all()
@classmethod
@async_db_query
async def async_list_by_page(cls, db: AsyncSession, page: Optional[int] = 1, count: Optional[int] = 30):
result = await db.execute(
select(cls).offset((page - 1) * count).limit(count)
)
async def async_list_by_page(
cls, db: AsyncSession, page: Optional[int] = 1, count: Optional[int] = 30
):
result = await db.execute(select(cls).offset((page - 1) * count).limit(count))
return result.scalars().all()
@classmethod
@async_db_query
async def async_list_by_title(
cls,
db: AsyncSession,
title: str,
page: Optional[int] = 1,
count: Optional[int] = 30,
):
query = (
select(cls).filter(cls.title.like(f"%{title}%")).order_by(cls.date.desc())
)
query = query.offset((page - 1) * count).limit(count)
result = await db.execute(query)
return result.scalars().all()
@classmethod
@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
async def async_count_by_title(cls, db: AsyncSession, title: str):
result = await db.execute(
select(func.count(cls.id)).filter(cls.title.like(f"%{title}%"))
)
return result.scalar()
@classmethod
@db_query
def get_by_path(cls, db: Session, path: str):
@@ -94,9 +136,16 @@ class DownloadHistory(Base):
@classmethod
@db_query
def get_last_by(cls, db: Session, mtype: Optional[str] = None, title: Optional[str] = None,
year: Optional[str] = None, season: Optional[str] = None,
episode: Optional[str] = None, tmdbid: Optional[int] = None):
def get_last_by(
cls,
db: Session,
mtype: Optional[str] = None,
title: Optional[str] = None,
year: Optional[str] = None,
season: Optional[str] = None,
episode: Optional[str] = None,
tmdbid: Optional[int] = None,
):
"""
据tmdbid、season、season_episode查询下载记录
tmdbid + mtype 或 title + year
@@ -105,42 +154,76 @@ class DownloadHistory(Base):
if tmdbid and mtype:
# 电视剧某季某集
if season is not None and episode:
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
DownloadHistory.type == mtype,
DownloadHistory.seasons == season,
DownloadHistory.episodes == episode).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.tmdbid == tmdbid,
DownloadHistory.type == mtype,
DownloadHistory.seasons == season,
DownloadHistory.episodes == episode,
)
.order_by(DownloadHistory.id.desc())
.all()
)
# 电视剧某季
elif season is not None:
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
DownloadHistory.type == mtype,
DownloadHistory.seasons == season).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.tmdbid == tmdbid,
DownloadHistory.type == mtype,
DownloadHistory.seasons == season,
)
.order_by(DownloadHistory.id.desc())
.all()
)
else:
# 电视剧所有季集/电影
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
DownloadHistory.type == mtype).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.tmdbid == tmdbid, DownloadHistory.type == mtype
)
.order_by(DownloadHistory.id.desc())
.all()
)
# 标题 + 年份
elif title and year:
# 电视剧某季某集
if season is not None and episode:
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
DownloadHistory.year == year,
DownloadHistory.seasons == season,
DownloadHistory.episodes == episode).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.title == title,
DownloadHistory.year == year,
DownloadHistory.seasons == season,
DownloadHistory.episodes == episode,
)
.order_by(DownloadHistory.id.desc())
.all()
)
# 电视剧某季
elif season is not None:
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
DownloadHistory.year == year,
DownloadHistory.seasons == season).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.title == title,
DownloadHistory.year == year,
DownloadHistory.seasons == season,
)
.order_by(DownloadHistory.id.desc())
.all()
)
else:
# 电视剧所有季集/电影
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
DownloadHistory.year == year).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.title == title, DownloadHistory.year == year
)
.order_by(DownloadHistory.id.desc())
.all()
)
return []
@@ -151,45 +234,80 @@ class DownloadHistory(Base):
查询某用户某时间之后的下载历史
"""
if username:
return db.query(DownloadHistory).filter(DownloadHistory.date < date,
DownloadHistory.username == username).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.date < date, DownloadHistory.username == username
)
.order_by(DownloadHistory.id.desc())
.all()
)
else:
return db.query(DownloadHistory).filter(DownloadHistory.date < date).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(DownloadHistory.date < date)
.order_by(DownloadHistory.id.desc())
.all()
)
@classmethod
@db_query
def list_by_date(cls, db: Session, date: str, type: str, tmdbid: str, seasons: Optional[str] = None):
def list_by_date(
cls,
db: Session,
date: str,
type: str,
tmdbid: str,
seasons: Optional[str] = None,
):
"""
查询某时间之后的下载历史
"""
if seasons:
return db.query(DownloadHistory).filter(DownloadHistory.date > date,
DownloadHistory.type == type,
DownloadHistory.tmdbid == tmdbid,
DownloadHistory.seasons == seasons).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.date > date,
DownloadHistory.type == type,
DownloadHistory.tmdbid == tmdbid,
DownloadHistory.seasons == seasons,
)
.order_by(DownloadHistory.id.desc())
.all()
)
else:
return db.query(DownloadHistory).filter(DownloadHistory.date > date,
DownloadHistory.type == type,
DownloadHistory.tmdbid == tmdbid).order_by(
DownloadHistory.id.desc()).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.date > date,
DownloadHistory.type == type,
DownloadHistory.tmdbid == tmdbid,
)
.order_by(DownloadHistory.id.desc())
.all()
)
@classmethod
@db_query
def list_by_type(cls, db: Session, mtype: str, days: int):
return db.query(DownloadHistory) \
.filter(DownloadHistory.type == mtype,
DownloadHistory.date >= time.strftime("%Y-%m-%d %H:%M:%S",
time.localtime(time.time() - 86400 * int(days)))
).all()
return (
db.query(DownloadHistory)
.filter(
DownloadHistory.type == mtype,
DownloadHistory.date
>= time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(time.time() - 86400 * int(days))
),
)
.all()
)
class DownloadFiles(Base):
"""
下载文件记录
"""
id = get_id_column()
# 下载器
downloader = Column(String)
@@ -210,8 +328,11 @@ class DownloadFiles(Base):
@db_query
def get_by_hash(cls, db: Session, download_hash: str, state: Optional[int] = None):
if state is not None:
return db.query(cls).filter(cls.download_hash == download_hash,
cls.state == state).all()
return (
db.query(cls)
.filter(cls.download_hash == download_hash, cls.state == state)
.all()
)
else:
return db.query(cls).filter(cls.download_hash == download_hash).all()
@@ -219,11 +340,19 @@ class DownloadFiles(Base):
@db_query
def get_by_fullpath(cls, db: Session, fullpath: str, all_files: bool = False):
if not all_files:
return db.query(cls).filter(cls.fullpath == fullpath).order_by(
cls.id.desc()).first()
return (
db.query(cls)
.filter(cls.fullpath == fullpath)
.order_by(cls.id.desc())
.first()
)
else:
return db.query(cls).filter(cls.fullpath == fullpath).order_by(
cls.id.desc()).all()
return (
db.query(cls)
.filter(cls.fullpath == fullpath)
.order_by(cls.id.desc())
.all()
)
@classmethod
@db_query
@@ -233,9 +362,6 @@ class DownloadFiles(Base):
@classmethod
@db_update
def delete_by_fullpath(cls, db: Session, fullpath: str):
db.query(cls).filter(cls.fullpath == fullpath,
cls.state == 1).update(
{
"state": 0
}
db.query(cls).filter(cls.fullpath == fullpath, cls.state == 1).update(
{"state": 0}
)