mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 16:36:53 +08:00
新增agent删除下载历史记录工具
This commit is contained in:
@@ -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.run_workflow import RunWorkflowTool
|
||||||
from app.agent.tools.impl.update_site_cookie import UpdateSiteCookieTool
|
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 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.modify_download import ModifyDownloadTool
|
||||||
from app.agent.tools.impl.query_directory_settings import QueryDirectorySettingsTool
|
from app.agent.tools.impl.query_directory_settings import QueryDirectorySettingsTool
|
||||||
from app.agent.tools.impl.list_directory import ListDirectoryTool
|
from app.agent.tools.impl.list_directory import ListDirectoryTool
|
||||||
@@ -95,6 +96,7 @@ class MoviePilotToolFactory:
|
|||||||
DeleteSubscribeTool,
|
DeleteSubscribeTool,
|
||||||
QueryDownloadTasksTool,
|
QueryDownloadTasksTool,
|
||||||
DeleteDownloadTool,
|
DeleteDownloadTool,
|
||||||
|
DeleteDownloadHistoryTool,
|
||||||
ModifyDownloadTool,
|
ModifyDownloadTool,
|
||||||
QueryDownloadersTool,
|
QueryDownloadersTool,
|
||||||
QuerySitesTool,
|
QuerySitesTool,
|
||||||
|
|||||||
@@ -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)}"
|
||||||
@@ -12,6 +12,7 @@ class DownloadHistory(Base):
|
|||||||
"""
|
"""
|
||||||
下载历史记录
|
下载历史记录
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = get_id_column()
|
id = get_id_column()
|
||||||
# 保存路径
|
# 保存路径
|
||||||
path = Column(String, nullable=False, index=True)
|
path = Column(String, nullable=False, index=True)
|
||||||
@@ -61,32 +62,73 @@ class DownloadHistory(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
def get_by_hash(cls, db: Session, download_hash: str):
|
def get_by_hash(cls, db: Session, download_hash: str):
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.download_hash == download_hash).order_by(
|
return (
|
||||||
DownloadHistory.date.desc()
|
db.query(DownloadHistory)
|
||||||
).first()
|
.filter(DownloadHistory.download_hash == download_hash)
|
||||||
|
.order_by(DownloadHistory.date.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
def get_by_mediaid(cls, db: Session, tmdbid: int, doubanid: str):
|
def get_by_mediaid(cls, db: Session, tmdbid: int, doubanid: str):
|
||||||
if tmdbid:
|
if tmdbid:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid).all()
|
return (
|
||||||
|
db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid).all()
|
||||||
|
)
|
||||||
elif doubanid:
|
elif doubanid:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.doubanid == doubanid).all()
|
return (
|
||||||
|
db.query(DownloadHistory)
|
||||||
|
.filter(DownloadHistory.doubanid == doubanid)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@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()
|
return db.query(DownloadHistory).offset((page - 1) * count).limit(count).all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@async_db_query
|
@async_db_query
|
||||||
async def async_list_by_page(cls, db: AsyncSession, page: Optional[int] = 1, count: Optional[int] = 30):
|
async def async_list_by_page(
|
||||||
result = await db.execute(
|
cls, db: AsyncSession, page: Optional[int] = 1, count: Optional[int] = 30
|
||||||
select(cls).offset((page - 1) * count).limit(count)
|
):
|
||||||
)
|
result = await db.execute(select(cls).offset((page - 1) * count).limit(count))
|
||||||
return result.scalars().all()
|
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
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
def get_by_path(cls, db: Session, path: str):
|
def get_by_path(cls, db: Session, path: str):
|
||||||
@@ -94,9 +136,16 @@ class DownloadHistory(Base):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
def get_last_by(cls, db: Session, mtype: Optional[str] = None, title: Optional[str] = None,
|
def get_last_by(
|
||||||
year: Optional[str] = None, season: Optional[str] = None,
|
cls,
|
||||||
episode: Optional[str] = None, tmdbid: Optional[int] = None):
|
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、season、season_episode查询下载记录
|
||||||
tmdbid + mtype 或 title + year
|
tmdbid + mtype 或 title + year
|
||||||
@@ -105,42 +154,76 @@ class DownloadHistory(Base):
|
|||||||
if tmdbid and mtype:
|
if tmdbid and mtype:
|
||||||
# 电视剧某季某集
|
# 电视剧某季某集
|
||||||
if season is not None and episode:
|
if season is not None and episode:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
return (
|
||||||
DownloadHistory.type == mtype,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.seasons == season,
|
.filter(
|
||||||
DownloadHistory.episodes == episode).order_by(
|
DownloadHistory.tmdbid == tmdbid,
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.type == mtype,
|
||||||
|
DownloadHistory.seasons == season,
|
||||||
|
DownloadHistory.episodes == episode,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# 电视剧某季
|
# 电视剧某季
|
||||||
elif season is not None:
|
elif season is not None:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
return (
|
||||||
DownloadHistory.type == mtype,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.seasons == season).order_by(
|
.filter(
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.tmdbid == tmdbid,
|
||||||
|
DownloadHistory.type == mtype,
|
||||||
|
DownloadHistory.seasons == season,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# 电视剧所有季集/电影
|
# 电视剧所有季集/电影
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.tmdbid == tmdbid,
|
return (
|
||||||
DownloadHistory.type == mtype).order_by(
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.id.desc()).all()
|
.filter(
|
||||||
|
DownloadHistory.tmdbid == tmdbid, DownloadHistory.type == mtype
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# 标题 + 年份
|
# 标题 + 年份
|
||||||
elif title and year:
|
elif title and year:
|
||||||
# 电视剧某季某集
|
# 电视剧某季某集
|
||||||
if season is not None and episode:
|
if season is not None and episode:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
return (
|
||||||
DownloadHistory.year == year,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.seasons == season,
|
.filter(
|
||||||
DownloadHistory.episodes == episode).order_by(
|
DownloadHistory.title == title,
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.year == year,
|
||||||
|
DownloadHistory.seasons == season,
|
||||||
|
DownloadHistory.episodes == episode,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# 电视剧某季
|
# 电视剧某季
|
||||||
elif season is not None:
|
elif season is not None:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
return (
|
||||||
DownloadHistory.year == year,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.seasons == season).order_by(
|
.filter(
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.title == title,
|
||||||
|
DownloadHistory.year == year,
|
||||||
|
DownloadHistory.seasons == season,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# 电视剧所有季集/电影
|
# 电视剧所有季集/电影
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.title == title,
|
return (
|
||||||
DownloadHistory.year == year).order_by(
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.id.desc()).all()
|
.filter(
|
||||||
|
DownloadHistory.title == title, DownloadHistory.year == year
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -151,45 +234,80 @@ class DownloadHistory(Base):
|
|||||||
查询某用户某时间之后的下载历史
|
查询某用户某时间之后的下载历史
|
||||||
"""
|
"""
|
||||||
if username:
|
if username:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.date < date,
|
return (
|
||||||
DownloadHistory.username == username).order_by(
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.id.desc()).all()
|
.filter(
|
||||||
|
DownloadHistory.date < date, DownloadHistory.username == username
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.date < date).order_by(
|
return (
|
||||||
DownloadHistory.id.desc()).all()
|
db.query(DownloadHistory)
|
||||||
|
.filter(DownloadHistory.date < date)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@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:
|
if seasons:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.date > date,
|
return (
|
||||||
DownloadHistory.type == type,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.tmdbid == tmdbid,
|
.filter(
|
||||||
DownloadHistory.seasons == seasons).order_by(
|
DownloadHistory.date > date,
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.type == type,
|
||||||
|
DownloadHistory.tmdbid == tmdbid,
|
||||||
|
DownloadHistory.seasons == seasons,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return db.query(DownloadHistory).filter(DownloadHistory.date > date,
|
return (
|
||||||
DownloadHistory.type == type,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.tmdbid == tmdbid).order_by(
|
.filter(
|
||||||
DownloadHistory.id.desc()).all()
|
DownloadHistory.date > date,
|
||||||
|
DownloadHistory.type == type,
|
||||||
|
DownloadHistory.tmdbid == tmdbid,
|
||||||
|
)
|
||||||
|
.order_by(DownloadHistory.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
def list_by_type(cls, db: Session, mtype: str, days: int):
|
def list_by_type(cls, db: Session, mtype: str, days: int):
|
||||||
return db.query(DownloadHistory) \
|
return (
|
||||||
.filter(DownloadHistory.type == mtype,
|
db.query(DownloadHistory)
|
||||||
DownloadHistory.date >= time.strftime("%Y-%m-%d %H:%M:%S",
|
.filter(
|
||||||
time.localtime(time.time() - 86400 * int(days)))
|
DownloadHistory.type == mtype,
|
||||||
).all()
|
DownloadHistory.date
|
||||||
|
>= time.strftime(
|
||||||
|
"%Y-%m-%d %H:%M:%S", time.localtime(time.time() - 86400 * int(days))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DownloadFiles(Base):
|
class DownloadFiles(Base):
|
||||||
"""
|
"""
|
||||||
下载文件记录
|
下载文件记录
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = get_id_column()
|
id = get_id_column()
|
||||||
# 下载器
|
# 下载器
|
||||||
downloader = Column(String)
|
downloader = Column(String)
|
||||||
@@ -210,8 +328,11 @@ class DownloadFiles(Base):
|
|||||||
@db_query
|
@db_query
|
||||||
def get_by_hash(cls, db: Session, download_hash: str, state: Optional[int] = None):
|
def get_by_hash(cls, db: Session, download_hash: str, state: Optional[int] = None):
|
||||||
if state is not None:
|
if state is not None:
|
||||||
return db.query(cls).filter(cls.download_hash == download_hash,
|
return (
|
||||||
cls.state == state).all()
|
db.query(cls)
|
||||||
|
.filter(cls.download_hash == download_hash, cls.state == state)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return db.query(cls).filter(cls.download_hash == download_hash).all()
|
return db.query(cls).filter(cls.download_hash == download_hash).all()
|
||||||
|
|
||||||
@@ -219,11 +340,19 @@ class DownloadFiles(Base):
|
|||||||
@db_query
|
@db_query
|
||||||
def get_by_fullpath(cls, db: Session, fullpath: str, all_files: bool = False):
|
def get_by_fullpath(cls, db: Session, fullpath: str, all_files: bool = False):
|
||||||
if not all_files:
|
if not all_files:
|
||||||
return db.query(cls).filter(cls.fullpath == fullpath).order_by(
|
return (
|
||||||
cls.id.desc()).first()
|
db.query(cls)
|
||||||
|
.filter(cls.fullpath == fullpath)
|
||||||
|
.order_by(cls.id.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return db.query(cls).filter(cls.fullpath == fullpath).order_by(
|
return (
|
||||||
cls.id.desc()).all()
|
db.query(cls)
|
||||||
|
.filter(cls.fullpath == fullpath)
|
||||||
|
.order_by(cls.id.desc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@db_query
|
@db_query
|
||||||
@@ -233,9 +362,6 @@ class DownloadFiles(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@db_update
|
@db_update
|
||||||
def delete_by_fullpath(cls, db: Session, fullpath: str):
|
def delete_by_fullpath(cls, db: Session, fullpath: str):
|
||||||
db.query(cls).filter(cls.fullpath == fullpath,
|
db.query(cls).filter(cls.fullpath == fullpath, cls.state == 1).update(
|
||||||
cls.state == 1).update(
|
{"state": 0}
|
||||||
{
|
|
||||||
"state": 0
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user