mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-16 21:20:32 +08:00
feat: unify download task tool names
This commit is contained in:
@@ -64,7 +64,7 @@ You act as a proactive agent. Your goal is to fully resolve the user's media-rel
|
||||
- For fuzzy torrent names, filenames, or manually provided paths, prefer `recognize_media` before asking the user for a cleaner title.
|
||||
- If `search_media` fails, fall back to `search_web` or `recognize_media`. Only ask the user when automated paths are exhausted.
|
||||
- If torrent search yields no useful result, check site scope, site health, and recognition quality before concluding that the resource is unavailable.
|
||||
- Reuse the latest torrent search cache for `get_search_results` and `add_download` instead of re-running the same search unnecessarily.
|
||||
- Reuse the latest torrent search cache for `get_search_results` and `add_download_tasks` instead of re-running the same search unnecessarily.
|
||||
- Use `execute_command` only for diagnostics, read-only inspection, or commands the user explicitly asked to run. Its default `action=start` starts a managed background session and returns `session_id`, `status`, `last_seq`, and `output_until_seq`; call the same tool again with `action=read`, `action=wait`, `action=write`, or `action=kill` to poll output, wait in short segments, send stdin, or stop the process.
|
||||
</tool_strategy>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import List, Callable
|
||||
|
||||
from app.agent.tools.impl.add_download import AddDownloadTool
|
||||
from app.agent.tools.impl.add_download_tasks import AddDownloadTasksTool
|
||||
from app.agent.tools.impl.add_subscribe import AddSubscribeTool
|
||||
from app.agent.tools.impl.update_subscribe import UpdateSubscribeTool
|
||||
from app.agent.tools.impl.search_subscribe import SearchSubscribeTool
|
||||
@@ -50,7 +50,7 @@ from app.agent.tools.impl.query_personas import QueryPersonasTool
|
||||
from app.agent.tools.impl.switch_persona import SwitchPersonaTool
|
||||
from app.agent.tools.impl.update_persona_definition import UpdatePersonaDefinitionTool
|
||||
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_tasks import DeleteDownloadTasksTool
|
||||
from app.agent.tools.impl.delete_download_history import DeleteDownloadHistoryTool
|
||||
from app.agent.tools.impl.delete_transfer_history import DeleteTransferHistoryTool
|
||||
from app.agent.tools.impl.update_download_tasks import UpdateDownloadTasksTool
|
||||
@@ -167,7 +167,7 @@ class MoviePilotToolFactory:
|
||||
GetSearchResultsTool,
|
||||
SearchWebTool,
|
||||
RecognizeCaptchaTool,
|
||||
AddDownloadTool,
|
||||
AddDownloadTasksTool,
|
||||
QuerySubscribesTool,
|
||||
QuerySubscribeSharesTool,
|
||||
QueryPopularSubscribesTool,
|
||||
@@ -183,7 +183,7 @@ class MoviePilotToolFactory:
|
||||
QuerySubscribeHistoryTool,
|
||||
DeleteSubscribeTool,
|
||||
QueryDownloadTasksTool,
|
||||
DeleteDownloadTool,
|
||||
DeleteDownloadTasksTool,
|
||||
DeleteDownloadHistoryTool,
|
||||
DeleteTransferHistoryTool,
|
||||
UpdateDownloadTasksTool,
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
"""添加下载工具"""
|
||||
"""添加下载任务工具"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Type
|
||||
from typing import List, Optional, Type, Union
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.agent.tools.base import MoviePilotTool
|
||||
from app.agent.tools.tags import ToolTag
|
||||
from app.chain.download import DownloadChain
|
||||
from app.chain.media import MediaChain
|
||||
from app.chain.search import SearchChain
|
||||
from app.chain.download import DownloadChain
|
||||
from app.core.config import settings
|
||||
from app.core.context import Context
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.db.site_oper import SiteOper
|
||||
from app.helper.directory import DirectoryHelper
|
||||
from app.log import logger
|
||||
from app.schemas import TorrentInfo, FileURI
|
||||
from app.schemas import FileURI, TorrentInfo
|
||||
from app.utils.crypto import HashUtils
|
||||
|
||||
|
||||
class AddDownloadInput(BaseModel):
|
||||
"""添加下载工具的输入参数模型"""
|
||||
class AddDownloadTasksInput(BaseModel):
|
||||
"""添加下载任务工具的输入参数模型"""
|
||||
|
||||
explanation: Optional[str] = Field(None, description="Clear explanation of why this tool is being used in the current context")
|
||||
torrent_url: List[str] = Field(
|
||||
...,
|
||||
@@ -36,15 +37,17 @@ class AddDownloadInput(BaseModel):
|
||||
description="Comma-separated list of labels/tags to assign to the download (optional, e.g., 'movie,hd,bluray')")
|
||||
|
||||
|
||||
class AddDownloadTool(MoviePilotTool):
|
||||
name: str = "add_download"
|
||||
class AddDownloadTasksTool(MoviePilotTool):
|
||||
"""添加下载任务工具"""
|
||||
|
||||
name: str = "add_download_tasks"
|
||||
tags: list[str] = [
|
||||
ToolTag.Write,
|
||||
ToolTag.Download,
|
||||
ToolTag.Resource,
|
||||
]
|
||||
description: str = "Add torrent download tasks using refs from get_search_results or magnet links."
|
||||
args_schema: Type[BaseModel] = AddDownloadInput
|
||||
args_schema: Type[BaseModel] = AddDownloadTasksInput
|
||||
|
||||
def get_tool_message(self, **kwargs) -> Optional[str]:
|
||||
"""根据下载参数生成友好的提示消息"""
|
||||
@@ -157,16 +160,16 @@ class AddDownloadTool(MoviePilotTool):
|
||||
prefix = "添加种子任务失败:"
|
||||
if normalized_error.startswith(prefix):
|
||||
normalized_error = normalized_error[len(prefix):].lstrip()
|
||||
if AddDownloadTool._is_magnet_link_input(normalized_error):
|
||||
if AddDownloadTasksTool._is_magnet_link_input(normalized_error):
|
||||
normalized_error = ""
|
||||
if normalized_error:
|
||||
return f"{torrent_ref} {normalized_error}"
|
||||
if AddDownloadTool._is_torrent_ref(torrent_ref):
|
||||
if AddDownloadTasksTool._is_torrent_ref(torrent_ref):
|
||||
return torrent_ref
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def _normalize_torrent_urls(cls, torrent_url: Optional[List[str] | str]) -> List[str]:
|
||||
def _normalize_torrent_urls(cls, torrent_url: Optional[Union[List[str], str]]) -> List[str]:
|
||||
"""统一规范 torrent_url 输入,保留所有非空值"""
|
||||
if torrent_url is None:
|
||||
return []
|
||||
@@ -234,6 +237,7 @@ class AddDownloadTool(MoviePilotTool):
|
||||
async def run(self, torrent_url: Optional[List[str]] = None,
|
||||
downloader: Optional[str] = None, save_path: Optional[str] = None,
|
||||
labels: Optional[str] = None, **kwargs) -> str:
|
||||
"""执行添加下载任务。"""
|
||||
logger.info(
|
||||
f"执行工具: {self.name}, 参数: torrent_url={torrent_url}, downloader={downloader}, save_path={save_path}, labels={labels}")
|
||||
|
||||
@@ -10,7 +10,7 @@ from app.chain.download import DownloadChain
|
||||
from app.log import logger
|
||||
|
||||
|
||||
class DeleteDownloadInput(BaseModel):
|
||||
class DeleteDownloadTasksInput(BaseModel):
|
||||
"""删除下载任务工具的输入参数模型"""
|
||||
|
||||
explanation: Optional[str] = Field(None,
|
||||
@@ -28,15 +28,17 @@ class DeleteDownloadInput(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class DeleteDownloadTool(MoviePilotTool):
|
||||
name: str = "delete_download"
|
||||
class DeleteDownloadTasksTool(MoviePilotTool):
|
||||
"""删除下载任务工具"""
|
||||
|
||||
name: str = "delete_download_tasks"
|
||||
tags: list[str] = [
|
||||
ToolTag.Write,
|
||||
ToolTag.Download,
|
||||
ToolTag.Admin,
|
||||
]
|
||||
description: str = "Delete a download task from the downloader by task hash only. Optionally specify the downloader name and whether to delete downloaded files."
|
||||
args_schema: Type[BaseModel] = DeleteDownloadInput
|
||||
args_schema: Type[BaseModel] = DeleteDownloadTasksInput
|
||||
require_admin: bool = True
|
||||
|
||||
def get_tool_message(self, **kwargs) -> Optional[str]:
|
||||
@@ -69,6 +71,7 @@ class DeleteDownloadTool(MoviePilotTool):
|
||||
delete_files: Optional[bool] = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""执行删除下载任务。"""
|
||||
logger.info(
|
||||
f"执行工具: {self.name}, 参数: hash={hash}, downloader={downloader}, delete_files={delete_files}"
|
||||
)
|
||||
@@ -238,8 +238,10 @@ MoviePilot 也提供普通 REST API 给前端和自动化客户端使用。所
|
||||
|
||||
**下载任务工具说明**:
|
||||
|
||||
- `add_download_tasks` 用于添加下载任务,支持 `get_search_results` 返回的 `hash:id` 引用和磁力链接,可指定下载器、保存目录和标签。
|
||||
- `query_download_tasks` 用于查询下载任务,支持按下载器、状态、Hash、标题、标签过滤;返回保存目录、内容路径、上传/下载速度、上传/下载限速、分类、分享率、做种时间等下载器可提供的字段。按 `hash` 查询或传入 `include_trackers=true` 时,会尽量返回 Tracker URL 列表。
|
||||
- `update_download_tasks` 用于修改下载任务,统一支持 `start`/`stop`、标签、上传/下载限速、Tracker、保存目录、分类、分享率、做种时间等字段;具体字段是否成功取决于下载器能力,返回结果会按操作项逐条标记成功或失败。
|
||||
- `delete_download_tasks` 用于删除下载任务,按任务 Hash 操作,可指定下载器,并可选择是否同时删除已下载文件。
|
||||
|
||||
### 3. 获取工具详情
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Always run `show <command>` before calling a command — parameter names are not
|
||||
|---|---|
|
||||
| Media Search | search_media, recognize_media, query_media_detail, get_recommendations, search_person, search_person_credits |
|
||||
| Torrent | search_torrents, get_search_results |
|
||||
| Download | add_download, query_download_tasks, update_download_tasks, delete_download, query_downloaders |
|
||||
| Download | add_download_tasks, query_download_tasks, update_download_tasks, delete_download_tasks, query_downloaders |
|
||||
| Subscription | add_subscribe, query_subscribes, update_subscribe, delete_subscribe, search_subscribe, query_subscribe_history, query_popular_subscribes, query_subscribe_shares |
|
||||
| Library | query_library_exists, query_library_latest, transfer_file, scrape_metadata, query_transfer_history |
|
||||
| Files | list_directory, query_directory_settings |
|
||||
@@ -95,7 +95,7 @@ If the media already exists in the library or is already subscribed, **stop** an
|
||||
#### 6. Add download
|
||||
|
||||
Download one or more torrents (`torrent_url` comes from `get_search_results` output):
|
||||
`node scripts/mp-cli.js add_download torrent_url="abc1234:1,def5678:2"`
|
||||
`node scripts/mp-cli.js add_download_tasks torrent_url="abc1234:1,def5678:2"`
|
||||
|
||||
#### Error handling
|
||||
|
||||
@@ -104,7 +104,7 @@ Download one or more torrents (`torrent_url` comes from `get_search_results` out
|
||||
| `search_media` empty | Retry with alternative title (English/original), inform user. Still empty → ask for title or TMDB ID. |
|
||||
| `search_torrents` empty | Inform user, ask whether to retry with different sites. |
|
||||
| `get_search_results` empty | Do not silently broaden filters. Suggest which filter to relax, ask before retrying. |
|
||||
| `add_download` fails | Run `query_downloaders` + `query_download_tasks` to diagnose, then report to user. |
|
||||
| `add_download_tasks` fails | Run `query_downloaders` + `query_download_tasks` to diagnose, then report to user. |
|
||||
|
||||
### Add Subscription
|
||||
|
||||
@@ -135,10 +135,10 @@ Add trackers to a download task:
|
||||
`node scripts/mp-cli.js update_download_tasks hash=<hash> trackers='https://tracker.example/announce,udp://tracker.example:80/announce'`
|
||||
|
||||
Delete a download task (confirm with user first — irreversible):
|
||||
`node scripts/mp-cli.js delete_download hash=<hash>`
|
||||
`node scripts/mp-cli.js delete_download_tasks hash=<hash>`
|
||||
|
||||
Delete a download task and also remove its files (confirm with user first — irreversible):
|
||||
`node scripts/mp-cli.js delete_download hash=<hash> delete_files=true`
|
||||
`node scripts/mp-cli.js delete_download_tasks hash=<hash> delete_files=true`
|
||||
|
||||
### Manage Subscriptions
|
||||
|
||||
|
||||
27
tests/test_agent_download_task_tool_names.py
Normal file
27
tests/test_agent_download_task_tool_names.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.agent.tools.factory import MoviePilotToolFactory
|
||||
|
||||
|
||||
def test_factory_registers_plural_download_task_tool_names():
|
||||
"""
|
||||
下载任务工具应统一使用 *_download_tasks 命名。
|
||||
"""
|
||||
with patch(
|
||||
"app.agent.tools.factory.PluginManager.get_plugin_agent_tools",
|
||||
return_value=[],
|
||||
):
|
||||
tools = MoviePilotToolFactory.create_tools(
|
||||
session_id="download-task-names",
|
||||
user_id="10001",
|
||||
)
|
||||
|
||||
tool_names = {tool.name for tool in tools}
|
||||
assert {
|
||||
"add_download_tasks",
|
||||
"query_download_tasks",
|
||||
"update_download_tasks",
|
||||
"delete_download_tasks",
|
||||
} <= tool_names
|
||||
assert "add_download" not in tool_names
|
||||
assert "delete_download" not in tool_names
|
||||
Reference in New Issue
Block a user