mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-04 06:15:14 +08:00
feat(agent): complete moviepilot api contracts
This commit is contained in:
@@ -119,6 +119,12 @@ API_FIRST_BATCH_OPERATION_SPECS: tuple[ApiOperationSpec, ...] = (
|
||||
_spec("subscription.history"),
|
||||
_write("subscription.delete", effect=ActionEffect.DESTRUCTIVE_WRITE, recovery=_DELETE_RECOVERABLE),
|
||||
_spec("download.add", effect=ActionEffect.EXTERNAL_SIDE_EFFECT, confirmation=_CONFIRM, recovery=_IDEMPOTENT),
|
||||
_spec(
|
||||
"download.artist_collection",
|
||||
effect=ActionEffect.EXTERNAL_SIDE_EFFECT,
|
||||
confirmation=_CONFIRM,
|
||||
recovery=_IDEMPOTENT,
|
||||
),
|
||||
_spec("download.tasks.active"),
|
||||
_spec("download.clients"),
|
||||
_spec("download.paths"),
|
||||
@@ -298,6 +304,9 @@ API_EXTENDED_OPERATION_SPECS: tuple[ApiOperationSpec, ...] = (
|
||||
_admin_read("dashboard.network"),
|
||||
_spec("media.sources"),
|
||||
_spec("media.recognize_file"),
|
||||
_admin_read("media.cache.get", sensitivity=ResultSensitivity.PRIVATE),
|
||||
_write("media.cache.delete", effect=ActionEffect.DESTRUCTIVE_WRITE, recovery=RecoveryMode.NONE),
|
||||
_write("media.cache.clear", effect=ActionEffect.DESTRUCTIVE_WRITE, recovery=RecoveryMode.NONE),
|
||||
_spec("media.classification.fields"),
|
||||
_spec("media.classification.policy.get"),
|
||||
_admin_read("media.classification.policy.validate"),
|
||||
@@ -395,6 +404,14 @@ API_EXTENDED_OPERATION_SPECS: tuple[ApiOperationSpec, ...] = (
|
||||
),
|
||||
_spec("subscription.user.list"),
|
||||
_spec("subscription.files", result_sensitivity=ResultSensitivity.PRIVATE),
|
||||
_spec("subscription.execution.list", result_sensitivity=ResultSensitivity.PRIVATE),
|
||||
_spec("subscription.execution.get", result_sensitivity=ResultSensitivity.PRIVATE),
|
||||
_user_write(
|
||||
"subscription.execution.cancel",
|
||||
effect=ActionEffect.EXTERNAL_SIDE_EFFECT,
|
||||
recovery=RecoveryMode.RECONCILE,
|
||||
sensitivity=ResultSensitivity.PRIVATE,
|
||||
),
|
||||
_user_write("subscription.share", effect=ActionEffect.EXTERNAL_SIDE_EFFECT, recovery=RecoveryMode.RECONCILE),
|
||||
_user_write(
|
||||
"subscription.share.delete",
|
||||
@@ -588,6 +605,7 @@ API_OPERATION_ROUTES: dict[str, ApiOperationRoute] = {
|
||||
"subscription.history": ApiOperationRoute("GET", "/api/v1/subscribe/history/{mtype}"),
|
||||
"subscription.delete": ApiOperationRoute("DELETE", "/api/v1/subscribe/{subscribe_id}"),
|
||||
"download.add": ApiOperationRoute("POST", "/api/v1/download/add"),
|
||||
"download.artist_collection": ApiOperationRoute("POST", "/api/v1/download/artist-collection"),
|
||||
"download.tasks.active": ApiOperationRoute("GET", "/api/v1/download/"),
|
||||
"download.clients": ApiOperationRoute("GET", "/api/v1/download/clients"),
|
||||
"download.paths": ApiOperationRoute("GET", "/api/v1/download/paths"),
|
||||
@@ -658,6 +676,9 @@ API_OPERATION_ROUTES: dict[str, ApiOperationRoute] = {
|
||||
"dashboard.network": ApiOperationRoute("GET", "/api/v1/dashboard/network"),
|
||||
"media.sources": ApiOperationRoute("GET", "/api/v1/media/source"),
|
||||
"media.recognize_file": ApiOperationRoute("GET", "/api/v1/media/recognize_file"),
|
||||
"media.cache.get": ApiOperationRoute("GET", "/api/v1/tmdb/cache"),
|
||||
"media.cache.delete": ApiOperationRoute("DELETE", "/api/v1/tmdb/cache/{cache_key}"),
|
||||
"media.cache.clear": ApiOperationRoute("DELETE", "/api/v1/tmdb/cache"),
|
||||
"media.classification.fields": ApiOperationRoute("GET", "/api/v1/media/classification/fields"),
|
||||
"media.classification.policy.get": ApiOperationRoute("GET", "/api/v1/media/classification/policy"),
|
||||
"media.classification.policy.update": ApiOperationRoute("PUT", "/api/v1/media/classification/policy"),
|
||||
@@ -703,6 +724,13 @@ API_OPERATION_ROUTES: dict[str, ApiOperationRoute] = {
|
||||
"subscription.history.delete": ApiOperationRoute("DELETE", "/api/v1/subscribe/history/{history_id}"),
|
||||
"subscription.user.list": ApiOperationRoute("GET", "/api/v1/subscribe/user/{username}"),
|
||||
"subscription.files": ApiOperationRoute("GET", "/api/v1/subscribe/files/{subscribe_id}"),
|
||||
"subscription.execution.list": ApiOperationRoute("GET", "/api/v1/subscribe/execution/batches"),
|
||||
"subscription.execution.get": ApiOperationRoute(
|
||||
"GET", "/api/v1/subscribe/execution/batches/{batch_id}"
|
||||
),
|
||||
"subscription.execution.cancel": ApiOperationRoute(
|
||||
"PUT", "/api/v1/subscribe/execution/batches/{batch_id}/cancel"
|
||||
),
|
||||
"subscription.share": ApiOperationRoute("POST", "/api/v1/subscribe/share"),
|
||||
"subscription.share.delete": ApiOperationRoute("DELETE", "/api/v1/subscribe/share/{share_id}"),
|
||||
"subscription.fork": ApiOperationRoute("POST", "/api/v1/subscribe/fork"),
|
||||
|
||||
@@ -12,6 +12,7 @@ OPERATION_DESCRIPTIONS = {
|
||||
"config.system.get": "Discover registered system settings or read one exact setting.",
|
||||
"config.system.update": "Update one exact registered system setting.",
|
||||
"download.add": "Submit one torrent to MoviePilot's normal download workflow.",
|
||||
"download.artist_collection": "Submit one artist-wide music torrent to MoviePilot's collection download workflow.",
|
||||
"download.clients": "List enabled downloader instance names and provider types without credentials.",
|
||||
"download.history.list": "Page MoviePilot download-history records in reverse chronological order.",
|
||||
"download.history.delete": "Delete one MoviePilot download-history record.",
|
||||
@@ -29,6 +30,9 @@ OPERATION_DESCRIPTIONS = {
|
||||
"library.exists": "Check configured media servers for one canonical media identity.",
|
||||
"library.latest": "List recently added items from one configured media-server instance for the current user.",
|
||||
"media.detail": "Read canonical media details from one selected metadata source.",
|
||||
"media.cache.get": "Inspect the administrator-only TMDB recognition cache and summary counts.",
|
||||
"media.cache.delete": "Delete one administrator-only TMDB recognition-cache entry by exact key.",
|
||||
"media.cache.clear": "Clear the complete administrator-only TMDB recognition cache.",
|
||||
"media.episode_schedule": "Read TMDB episode release information for one season.",
|
||||
"media.person.credits": "Read one person's credits from the selected metadata source.",
|
||||
"media.person.search": "Search people and music artists across selected metadata sources.",
|
||||
@@ -154,6 +158,9 @@ OPERATION_DESCRIPTIONS = {
|
||||
"storage.rename": "Rename one exact storage item, optionally applying media-aware recursive renaming.",
|
||||
"subscription.delete_by_media": "Delete accessible subscriptions matching one canonical media identity.",
|
||||
"subscription.files": "Read local library and transfer-file coverage for one accessible subscription.",
|
||||
"subscription.execution.list": "List recent durable subscription-search batches visible to the current user.",
|
||||
"subscription.execution.get": "Read one durable subscription-search batch visible to the current user.",
|
||||
"subscription.execution.cancel": "Request cancellation of one durable subscription-search batch before its download side-effect boundary.",
|
||||
"subscription.find": "Find one accessible subscription by canonical media identity and optional season.",
|
||||
"subscription.follow.add": "Follow one subscription-sharing user by exact share user ID.",
|
||||
"subscription.follow.delete": "Stop following one subscription-sharing user by exact share user ID.",
|
||||
@@ -230,6 +237,7 @@ FIELD_DESCRIPTIONS = {
|
||||
"apikey": "Site API key used by sites that support API-key authentication.",
|
||||
"area": "Optional region filter applied by the torrent search workflow.",
|
||||
"artist_id": "Source-native artist ID returned by music search or an album detail response.",
|
||||
"artist_name": "Artist name preserved as the identity label for an artist-wide music collection.",
|
||||
"audio_format": "Requested or recorded audio container or codec, such as FLAC or MP3.",
|
||||
"audio_lossless": "Whether the recorded audio result is lossless.",
|
||||
"audio_quality": "Subscription audio-quality rule, such as hires, lossless, or lossy.",
|
||||
@@ -246,7 +254,7 @@ FIELD_DESCRIPTIONS = {
|
||||
"scalar and object request bodies are not interchangeable."
|
||||
),
|
||||
"category": "MoviePilot media category or filter-group category, depending on the operation.",
|
||||
"cache_key": "Exact recognition-cache key returned by music.cache.get.",
|
||||
"cache_key": "Exact recognition-cache key returned by the corresponding recognition-cache get operation.",
|
||||
"channel": "Message channel that originally submitted the download.",
|
||||
"children": "Child storage items nested below this item.",
|
||||
"code": "Two-factor verification code or site-specific authentication secret.",
|
||||
@@ -331,6 +339,7 @@ FIELD_DESCRIPTIONS = {
|
||||
"limit_count": "Maximum number of site requests allowed in one rate-limit interval.",
|
||||
"limit_interval": "Number of requests in the site's rate-limit window.",
|
||||
"limit_seconds": "Site rate-limit window length in seconds.",
|
||||
"limit": "Maximum number of recent subscription-search batches to return.",
|
||||
"logid": "One download-history or transfer-log identifier used by manual transfer.",
|
||||
"logids": "Multiple download-history or transfer-log identifiers included in manual transfer.",
|
||||
"match_field": "Object field used to match one list item during an upsert or removal.",
|
||||
@@ -624,6 +633,9 @@ FIELD_DESCRIPTIONS.update(
|
||||
MODEL_DESCRIPTIONS = {
|
||||
"AgentCommandRunRequest": "Slash-command execution request.",
|
||||
"Body_add_api_v1_download_add_post": "MoviePilot download submission request.",
|
||||
"Body_download_artist_collection_api_v1_download_artist_collection_post": (
|
||||
"Artist-wide music collection download submission request."
|
||||
),
|
||||
"CustomFilterRuleCreateRequest": "Custom filter-rule creation request.",
|
||||
"CustomFilterRuleUpdateRequest": "Custom filter-rule update request.",
|
||||
"CustomIdentifiersUpdateRequest": "Complete custom recognition-identifier replacement request.",
|
||||
|
||||
@@ -449,6 +449,63 @@
|
||||
"title": "Body_add_api_v1_download_add_post",
|
||||
"type": "object"
|
||||
},
|
||||
"Body_download_artist_collection_api_v1_download_artist_collection_post": {
|
||||
"description": "Artist-wide music collection download submission request.",
|
||||
"properties": {
|
||||
"artist_id": {
|
||||
"description": "Source-native artist ID returned by music search or an album detail response.",
|
||||
"minLength": 1,
|
||||
"title": "Artist Id",
|
||||
"type": "string"
|
||||
},
|
||||
"artist_name": {
|
||||
"description": "Artist name preserved as the identity label for an artist-wide music collection.",
|
||||
"minLength": 1,
|
||||
"title": "Artist Name",
|
||||
"type": "string"
|
||||
},
|
||||
"downloader": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Configured downloader instance name.",
|
||||
"title": "Downloader"
|
||||
},
|
||||
"media_source": {
|
||||
"$ref": "#/$defs/MediaSource",
|
||||
"description": "Metadata source identifier. Preserve the exact value returned with media_id."
|
||||
},
|
||||
"save_path": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Configured downloader-side save path for the download or subscription.",
|
||||
"title": "Save Path"
|
||||
},
|
||||
"torrent_in": {
|
||||
"$ref": "#/$defs/TorrentInfo",
|
||||
"description": "Complete torrent candidate returned by search.results or search.torrents."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"artist_name",
|
||||
"artist_id",
|
||||
"media_source",
|
||||
"torrent_in"
|
||||
],
|
||||
"title": "Body_download_artist_collection_api_v1_download_artist_collection_post",
|
||||
"type": "object"
|
||||
},
|
||||
"Body_recommend_search_results_api_v1_search_recommend_post": {
|
||||
"description": "Torrent search results and recommendation controls supplied to the configured model.",
|
||||
"properties": {
|
||||
@@ -7020,6 +7077,27 @@
|
||||
"title": "download.add",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Submit one artist-wide music torrent to MoviePilot's collection download workflow. Method: POST. Path: /api/v1/download/artist-collection. Effect: external_side_effect.",
|
||||
"properties": {
|
||||
"body": {
|
||||
"$ref": "#/$defs/Body_download_artist_collection_api_v1_download_artist_collection_post",
|
||||
"description": "Request value for download.artist_collection. Submit one artist-wide music torrent to MoviePilot's collection download workflow. Use the exact type and fields below."
|
||||
},
|
||||
"operation_id": {
|
||||
"const": "download.artist_collection",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id",
|
||||
"body"
|
||||
],
|
||||
"title": "download.artist_collection",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "List enabled downloader instance names and provider types without credentials. Method: GET. Path: /api/v1/download/clients. Effect: safe_read. Collection response: data remains a list; omit both page and count to preserve the legacy complete result. Successful gateway output adds collection.result_count and the exact collection.total_count. For a count or summary, send page=1 and count=1, then read collection.total_count; do not query the database merely because item data is truncated.",
|
||||
@@ -7778,6 +7856,70 @@
|
||||
"total_count_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Clear the complete administrator-only TMDB recognition cache. Method: DELETE. Path: /api/v1/tmdb/cache. Effect: destructive_write.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "media.cache.clear",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id"
|
||||
],
|
||||
"title": "media.cache.clear",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Delete one administrator-only TMDB recognition-cache entry by exact key. Method: DELETE. Path: /api/v1/tmdb/cache/{cache_key}. Effect: destructive_write.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "media.cache.delete",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
},
|
||||
"path_params": {
|
||||
"additionalProperties": false,
|
||||
"description": "Resource identity placeholders for media.cache.delete. Delete one administrator-only TMDB recognition-cache entry by exact key. Use only the named fields below.",
|
||||
"properties": {
|
||||
"cache_key": {
|
||||
"description": "Exact recognition-cache key returned by the corresponding recognition-cache get operation.",
|
||||
"title": "Cache Key",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cache_key"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id",
|
||||
"path_params"
|
||||
],
|
||||
"title": "media.cache.delete",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Inspect the administrator-only TMDB recognition cache and summary counts. Method: GET. Path: /api/v1/tmdb/cache. Effect: safe_read.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "media.cache.get",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id"
|
||||
],
|
||||
"title": "media.cache.get",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Read the media-classification field catalog and source capabilities. Method: GET. Path: /api/v1/media/classification/fields. Effect: safe_read.",
|
||||
@@ -9149,7 +9291,7 @@
|
||||
"description": "Resource identity placeholders for music.cache.delete. Delete one administrator-only MusicBrainz recognition-cache entry by exact key. Use only the named fields below.",
|
||||
"properties": {
|
||||
"cache_key": {
|
||||
"description": "Exact recognition-cache key returned by music.cache.get.",
|
||||
"description": "Exact recognition-cache key returned by the corresponding recognition-cache get operation.",
|
||||
"title": "Cache Key",
|
||||
"type": "string"
|
||||
}
|
||||
@@ -12428,6 +12570,105 @@
|
||||
"title": "subscription.delete_by_media",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Request cancellation of one durable subscription-search batch before its download side-effect boundary. Method: PUT. Path: /api/v1/subscribe/execution/batches/{batch_id}/cancel. Effect: external_side_effect.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "subscription.execution.cancel",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
},
|
||||
"path_params": {
|
||||
"additionalProperties": false,
|
||||
"description": "Resource identity placeholders for subscription.execution.cancel. Request cancellation of one durable subscription-search batch before its download side-effect boundary. Use only the named fields below.",
|
||||
"properties": {
|
||||
"batch_id": {
|
||||
"description": "Stable subscription search batch identifier.",
|
||||
"title": "Batch Id",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"batch_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id",
|
||||
"path_params"
|
||||
],
|
||||
"title": "subscription.execution.cancel",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Read one durable subscription-search batch visible to the current user. Method: GET. Path: /api/v1/subscribe/execution/batches/{batch_id}. Effect: safe_read.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "subscription.execution.get",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
},
|
||||
"path_params": {
|
||||
"additionalProperties": false,
|
||||
"description": "Resource identity placeholders for subscription.execution.get. Read one durable subscription-search batch visible to the current user. Use only the named fields below.",
|
||||
"properties": {
|
||||
"batch_id": {
|
||||
"description": "Stable subscription search batch identifier.",
|
||||
"title": "Batch Id",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"batch_id"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id",
|
||||
"path_params"
|
||||
],
|
||||
"title": "subscription.execution.get",
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "List recent durable subscription-search batches visible to the current user. Method: GET. Path: /api/v1/subscribe/execution/batches. Effect: safe_read. Collection response: data remains a list and successful gateway output adds collection.result_count. collection.total_count is omitted when the endpoint or its upstream source does not expose a total.",
|
||||
"properties": {
|
||||
"operation_id": {
|
||||
"const": "subscription.execution.list",
|
||||
"description": "Exact allowlisted MoviePilot operation ID selecting this oneOf branch.",
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"additionalProperties": false,
|
||||
"description": "Filters and control values for subscription.execution.list. List recent durable subscription-search batches visible to the current user. Use only the named fields below.",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"default": 10,
|
||||
"description": "Maximum number of recent subscription-search batches to return.",
|
||||
"title": "Limit",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"operation_id"
|
||||
],
|
||||
"title": "subscription.execution.list",
|
||||
"type": "object",
|
||||
"x-moviepilot-collection": {
|
||||
"body_shape": "list",
|
||||
"default_pagination": "endpoint-defined",
|
||||
"result_count_field": "collection.result_count",
|
||||
"total_count_field": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"description": "Read local library and transfer-file coverage for one accessible subscription. Method: GET. Path: /api/v1/subscribe/files/{subscribe_id}. Effect: safe_read.",
|
||||
@@ -15458,6 +15699,7 @@
|
||||
"database.backups.list",
|
||||
"database.backups.verify",
|
||||
"download.add",
|
||||
"download.artist_collection",
|
||||
"download.clients",
|
||||
"download.history.delete",
|
||||
"download.history.list",
|
||||
@@ -15475,6 +15717,9 @@
|
||||
"filter.test",
|
||||
"library.exists",
|
||||
"library.latest",
|
||||
"media.cache.clear",
|
||||
"media.cache.delete",
|
||||
"media.cache.get",
|
||||
"media.classification.fields",
|
||||
"media.classification.policy.get",
|
||||
"media.classification.policy.history",
|
||||
@@ -15576,6 +15821,9 @@
|
||||
"subscription.add",
|
||||
"subscription.delete",
|
||||
"subscription.delete_by_media",
|
||||
"subscription.execution.cancel",
|
||||
"subscription.execution.get",
|
||||
"subscription.execution.list",
|
||||
"subscription.files",
|
||||
"subscription.find",
|
||||
"subscription.follow.add",
|
||||
|
||||
@@ -56,25 +56,25 @@ MCP 当前不会主动发送工具列表变更通知(`listChanged=false`)。
|
||||
|
||||
`app/agent/policy/resources/api_mcp_schema.json` 是 `moviepilot_api` 的生成制品,不是设置项或 API 参数的手工事实源。`scripts/generate_agent_api_mcp_schema.py` 从当前 FastAPI OpenAPI、固定 operation 路由和 Agent 专用英文参数说明生成该文件;运行时直接读取它响应外部 MCP `tools/list`,测试会校验生成结果没有漂移。修改 API、请求模型或 operation 后应重新生成并提交该文件,不应直接编辑 JSON。
|
||||
|
||||
当前完整 FastAPI OpenAPI 包含 397 个 HTTP 操作,其中 211 个稳定业务操作进入
|
||||
`moviepilot_api`,使用 209 个固定路由模板:208 条 OpenAPI 路由直接匹配,另有 1 条只允许
|
||||
当前完整 FastAPI OpenAPI 包含 397 个 HTTP 操作,其中 218 个稳定业务操作进入
|
||||
`moviepilot_api`,使用 216 个固定路由模板:215 条 OpenAPI 路由直接匹配,另有 1 条只允许
|
||||
`tmdb`、`douban`、`bangumi`、`anilist` 四个来源的受限人物作品动态路由。每个 operation
|
||||
均同时具备固定 method/path、角色权限、副作用等级、确认与恢复策略、结果敏感性、英文用途说明,
|
||||
以及可直接提交的 path/query/body JSON Schema;Skill front matter、正文 operation 章节、运行时
|
||||
注册表和 MCP `tools/list` 的 211 个 `oneOf` 分支必须完全一致。
|
||||
注册表和 MCP `tools/list` 的 218 个 `oneOf` 分支必须完全一致。
|
||||
|
||||
数量不相等是明确的安全与语义边界,而不是漏生成。当前 397 条路由均被审计并锁定为以下一种
|
||||
归属,审计生成器不再提供“未归类”兜底:
|
||||
|
||||
| 归属 | 数量 | Agent 使用方式 |
|
||||
| :--- | ---: | :--- |
|
||||
| `gateway` | 208 | 通过 `moviepilot_api` 的稳定 operation 和精确参数合同调用 |
|
||||
| `consolidated` | 74 | 通过同领域聚合 operation 调用,不复制数据源或前端专用路由 |
|
||||
| `provider-skill` | 13 | 通过下载器或媒体服务器 Skill 调用第三方 provider API |
|
||||
| `gateway` | 215 | 通过 `moviepilot_api` 的稳定 operation 和精确参数合同调用 |
|
||||
| `consolidated` | 71 | 通过同领域聚合 operation 调用,不复制数据源或前端专用路由 |
|
||||
| `provider-skill` | 12 | 通过下载器或媒体服务器 Skill 调用第三方 provider API |
|
||||
| `alternate-auth-duplicate` | 11 | 使用对应 bearer-authenticated gateway operation,不暴露 API_TOKEN 兼容副本 |
|
||||
| `transport_or_identity` | 66 | 由登录、令牌、MCP、会话、回调、健康检查等宿主传输/身份边界拥有 |
|
||||
| `stream_or_binary` | 10 | 由直接客户端处理流式日志、消息、文件、图片等非结构化响应 |
|
||||
| `ui_presentation` | 15 | 由前端或插件渲染面拥有,不作为业务 Agent operation |
|
||||
| `ui_presentation` | 12 | 由前端或插件渲染面拥有,不作为业务 Agent operation |
|
||||
|
||||
逐路由归属见 `docs/refactor/agent-api-surface-audit.md`,并由
|
||||
`tests/test_agent_api_surface_audit.py` 对当前 OpenAPI、固定注册表、MCP schema、英文 Skill
|
||||
@@ -454,12 +454,12 @@ AMLL 使用无需鉴权的原生搜索与获取接口,先尝试 ISRC,再核
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| GET | `/api/v1/tmdb/cache` | 查询 TheMovieDb 识别缓存统计、共享识别累计成功命中次数及开关状态 |
|
||||
| DELETE | `/api/v1/tmdb/cache/{cache_key}` | 按缓存键删除单条 TheMovieDb 识别缓存,缓存键需要进行 URL 编码 |
|
||||
| DELETE | `/api/v1/tmdb/cache` | 清空全部 TheMovieDb 识别缓存 |
|
||||
| GET | `/api/v1/music/cache` | 查询 MusicBrainz 音乐识别缓存统计及条目列表 |
|
||||
| DELETE | `/api/v1/music/cache/{cache_key}` | 按缓存键删除单条音乐识别缓存,缓存键需要进行 URL 编码 |
|
||||
| DELETE | `/api/v1/music/cache` | 清空全部音乐识别缓存 |
|
||||
| GET | `/api/v1/tmdb/cache` | `media.cache.get`:查询 TheMovieDb 识别缓存统计、共享识别累计成功命中次数及开关状态 |
|
||||
| DELETE | `/api/v1/tmdb/cache/{cache_key}` | `media.cache.delete`:按缓存键删除单条 TheMovieDb 识别缓存,缓存键需要进行 URL 编码 |
|
||||
| DELETE | `/api/v1/tmdb/cache` | `media.cache.clear`:清空全部 TheMovieDb 识别缓存 |
|
||||
| GET | `/api/v1/music/cache` | `music.cache.get`:查询 MusicBrainz 音乐识别缓存统计及条目列表 |
|
||||
| DELETE | `/api/v1/music/cache/{cache_key}` | `music.cache.delete`:按缓存键删除单条音乐识别缓存,缓存键需要进行 URL 编码 |
|
||||
| DELETE | `/api/v1/music/cache` | `music.cache.clear`:清空全部音乐识别缓存 |
|
||||
|
||||
TMDB 缓存查询响应的 `data` 包含 `count`、`recognized`、`unrecognized`、`data`,以及共享识别统计字段
|
||||
`shared_recognized` 和开关字段 `shared_recognize_enabled`。共享命中次数仅在共享结果驱动的二次媒体识别成功后累计。
|
||||
@@ -470,6 +470,18 @@ TMDB 缓存查询响应的 `data` 包含 `count`、`recognized`、`unrecognized`
|
||||
旧版未包含这些证据的派生缓存在升级后重新建立,不影响下载历史或订阅数据。
|
||||
名称确认规则更新时同样重建旧派生缓存,避免艺术家前后缀误截断的旧结果继续命中。
|
||||
|
||||
### 订阅搜索执行批次
|
||||
|
||||
订阅搜索会持久化为可恢复的执行批次,Agent 可以使用以下 `moviepilot_api` operation 查询或停止当前用户可见的批次:
|
||||
|
||||
| Operation | 方法 | 路径 | 说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `subscription.execution.list` | GET | `/api/v1/subscribe/execution/batches` | 查询最近的订阅搜索批次,`limit` 默认 10 |
|
||||
| `subscription.execution.get` | GET | `/api/v1/subscribe/execution/batches/{batch_id}` | 查询一个批次的状态、进度、恢复信息和最终结果 |
|
||||
| `subscription.execution.cancel` | PUT | `/api/v1/subscribe/execution/batches/{batch_id}/cancel` | 在下载副作用边界前请求取消一个批次 |
|
||||
|
||||
取消是幂等的状态请求,不会撤销已经提交到下载器的任务;批次详情中的状态和任务结果才是最终事实。
|
||||
|
||||
### 单条订阅搜索周期
|
||||
|
||||
`POST /api/v1/subscribe/` 和 `PUT /api/v1/subscribe/` 支持 `search_interval`:
|
||||
@@ -600,8 +612,9 @@ Web Agent 直接调用 `moviepilot_api` 时,宿主会自动加载 `moviepilot-
|
||||
| :--- | :--- |
|
||||
| 媒体/搜索 | `media.search`、`media.person.search`、`media.person.credits`、`media.recognize`、`media.scrape`、`media.episode_schedule`、`media.detail`、`search.torrents`、`search.results`、`recommendation.list` |
|
||||
| 媒体自动分类 | `media.classification.fields`、`media.classification.policy.get`、`media.classification.policy.validate`、`media.classification.policy.preview`、`media.classification.policy.impact`、`media.classification.policy.history`、`media.classification.policy.update`、`media.classification.policy.rollback` |
|
||||
| 订阅 | `subscription.add`、`subscription.update`、`subscription.search`、`subscription.list`、`subscription.shares`、`subscription.popular`、`subscription.history`、`subscription.delete` |
|
||||
| 下载/历史 | `download.add`、`download.history.delete`、`transfer.history.delete` |
|
||||
| 订阅 | `subscription.add`、`subscription.update`、`subscription.search`、`subscription.list`、`subscription.shares`、`subscription.popular`、`subscription.history`、`subscription.delete`、`subscription.execution.list`、`subscription.execution.get`、`subscription.execution.cancel` |
|
||||
| 下载/历史 | `download.add`、`download.artist_collection`、`download.history.delete`、`transfer.history.delete` |
|
||||
| 媒体缓存 | `media.cache.get`、`media.cache.delete`、`media.cache.clear`、`music.cache.get`、`music.cache.delete`、`music.cache.clear` |
|
||||
| 媒体库/存储/转移 | `library.exists`、`storage.settings`、`storage.list`、`transfer.history`、`transfer.file` |
|
||||
| 站点 | `site.list`、`site.update`、`site.userdata`、`site.test`、`site.cookie.update` |
|
||||
| 调度/工作流 | `scheduler.list`、`scheduler.run`、`workflow.list`、`workflow.run` |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"disposition_counts": {
|
||||
"alternate-auth-duplicate": 11,
|
||||
"consolidated": 74,
|
||||
"gateway": 208,
|
||||
"provider-skill": 13,
|
||||
"consolidated": 71,
|
||||
"gateway": 215,
|
||||
"provider-skill": 12,
|
||||
"stream_or_binary": 10,
|
||||
"transport_or_identity": 66,
|
||||
"ui_presentation": 15
|
||||
"ui_presentation": 12
|
||||
},
|
||||
"dynamic_gateway_routes": [
|
||||
{
|
||||
@@ -18,9 +18,9 @@
|
||||
"reason": "The executor validates and expands this bounded source placeholder to one of tmdb, douban, bangumi, or anilist before calling the corresponding concrete OpenAPI route."
|
||||
}
|
||||
],
|
||||
"gateway_http_route_count": 209,
|
||||
"gateway_operation_count": 211,
|
||||
"matched_gateway_http_route_count": 208,
|
||||
"gateway_http_route_count": 216,
|
||||
"gateway_operation_count": 218,
|
||||
"matched_gateway_http_route_count": 215,
|
||||
"openapi_operation_count": 397,
|
||||
"operations": [
|
||||
{
|
||||
@@ -640,12 +640,14 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "provider-skill",
|
||||
"disposition": "gateway",
|
||||
"method": "POST",
|
||||
"operation_ids": [],
|
||||
"owner": "downloader-operation",
|
||||
"operation_ids": [
|
||||
"download.artist_collection"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/download/artist-collection",
|
||||
"reason": "Low-level provider behavior is exposed by the self-describing provider Skill; high-level MoviePilot operations remain in moviepilot-api.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "添加艺术家合集下载",
|
||||
"tags": [
|
||||
"download"
|
||||
@@ -3616,36 +3618,42 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "ui_presentation",
|
||||
"disposition": "gateway",
|
||||
"method": "GET",
|
||||
"operation_ids": [],
|
||||
"owner": "host-ui",
|
||||
"operation_ids": [
|
||||
"subscription.execution.list"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/subscribe/execution/batches",
|
||||
"reason": "Background subscription execution status and cancellation are owned by the authenticated frontend workflow; they are not yet a stable Agent gateway contract.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "查看订阅搜索进度",
|
||||
"tags": [
|
||||
"subscribe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "ui_presentation",
|
||||
"disposition": "gateway",
|
||||
"method": "GET",
|
||||
"operation_ids": [],
|
||||
"owner": "host-ui",
|
||||
"operation_ids": [
|
||||
"subscription.execution.get"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/subscribe/execution/batches/{batch_id}",
|
||||
"reason": "Background subscription execution status and cancellation are owned by the authenticated frontend workflow; they are not yet a stable Agent gateway contract.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "查看一次订阅搜索",
|
||||
"tags": [
|
||||
"subscribe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "ui_presentation",
|
||||
"disposition": "gateway",
|
||||
"method": "PUT",
|
||||
"operation_ids": [],
|
||||
"owner": "host-ui",
|
||||
"operation_ids": [
|
||||
"subscription.execution.cancel"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/subscribe/execution/batches/{batch_id}/cancel",
|
||||
"reason": "Background subscription execution status and cancellation are owned by the authenticated frontend workflow; they are not yet a stable Agent gateway contract.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "停止一次订阅搜索",
|
||||
"tags": [
|
||||
"subscribe"
|
||||
@@ -4502,36 +4510,42 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "consolidated",
|
||||
"disposition": "gateway",
|
||||
"method": "DELETE",
|
||||
"operation_ids": [],
|
||||
"operation_ids": [
|
||||
"media.cache.clear"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/tmdb/cache",
|
||||
"reason": "Source-specific or presentation-oriented route is represented by a stable aggregate search, detail, person, recommendation, or music operation instead of duplicating every frontend route.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "清空 TheMovieDb 识别缓存",
|
||||
"tags": [
|
||||
"tmdb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "consolidated",
|
||||
"disposition": "gateway",
|
||||
"method": "GET",
|
||||
"operation_ids": [],
|
||||
"operation_ids": [
|
||||
"media.cache.get"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/tmdb/cache",
|
||||
"reason": "Source-specific or presentation-oriented route is represented by a stable aggregate search, detail, person, recommendation, or music operation instead of duplicating every frontend route.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "查询 TheMovieDb 识别缓存",
|
||||
"tags": [
|
||||
"tmdb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"disposition": "consolidated",
|
||||
"disposition": "gateway",
|
||||
"method": "DELETE",
|
||||
"operation_ids": [],
|
||||
"operation_ids": [
|
||||
"media.cache.delete"
|
||||
],
|
||||
"owner": "moviepilot-api",
|
||||
"path": "/api/v1/tmdb/cache/{cache_key}",
|
||||
"reason": "Source-specific or presentation-oriented route is represented by a stable aggregate search, detail, person, recommendation, or music operation instead of duplicating every frontend route.",
|
||||
"reason": "Executable through moviepilot_api; exact inputs are generated into MCP tools/list and SKILL.md.",
|
||||
"summary": "删除指定 TheMovieDb 识别缓存",
|
||||
"tags": [
|
||||
"tmdb"
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
## Result
|
||||
|
||||
- OpenAPI HTTP operations: **397**
|
||||
- Stable `moviepilot_api` operations: **211**
|
||||
- Exact HTTP routes used by the gateway: **209**
|
||||
- OpenAPI routes matched directly by the gateway: **208**
|
||||
- Stable `moviepilot_api` operations: **218**
|
||||
- Exact HTTP routes used by the gateway: **216**
|
||||
- OpenAPI routes matched directly by the gateway: **215**
|
||||
- Bounded dynamic gateway routes: **1**
|
||||
- Every gateway operation has a generated English oneOf input contract in MCP `tools/list` and `skills/moviepilot-api/SKILL.md`.
|
||||
- Every non-gateway OpenAPI operation is listed below with an explicit ownership boundary; it is not silently callable through arbitrary URL/method input.
|
||||
@@ -18,12 +18,12 @@
|
||||
| disposition | count | meaning |
|
||||
| :--- | ---: | :--- |
|
||||
| `alternate-auth-duplicate` | 11 | API-token compatibility duplicate of a bearer-authenticated capability. |
|
||||
| `consolidated` | 74 | Source/UI route represented by a stable aggregate Agent operation. |
|
||||
| `gateway` | 208 | Approved structured MoviePilot Agent operation. |
|
||||
| `provider-skill` | 13 | Low-level downloader or media-server capability owned by a provider Skill. |
|
||||
| `consolidated` | 71 | Source/UI route represented by a stable aggregate Agent operation. |
|
||||
| `gateway` | 215 | Approved structured MoviePilot Agent operation. |
|
||||
| `provider-skill` | 12 | Low-level downloader or media-server capability owned by a provider Skill. |
|
||||
| `stream_or_binary` | 10 | Streaming or binary response owned by a direct client transport. |
|
||||
| `transport_or_identity` | 66 | Authentication, protocol, callback, account, or conversation transport boundary. |
|
||||
| `ui_presentation` | 15 | Frontend or plugin-rendered presentation contract. |
|
||||
| `ui_presentation` | 12 | Frontend or plugin-rendered presentation contract. |
|
||||
|
||||
## Bounded Dynamic Routes
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
| `GET` | `/api/v1/download/` | download | `gateway` | download.tasks.active | 正在下载 |
|
||||
| `POST` | `/api/v1/download/` | download | `consolidated` | download.add | 添加下载(含媒体信息) |
|
||||
| `POST` | `/api/v1/download/add` | download | `gateway` | download.add | 添加下载(不含媒体信息) |
|
||||
| `POST` | `/api/v1/download/artist-collection` | download | `provider-skill` | downloader-operation | 添加艺术家合集下载 |
|
||||
| `POST` | `/api/v1/download/artist-collection` | download | `gateway` | download.artist_collection | 添加艺术家合集下载 |
|
||||
| `GET` | `/api/v1/download/clients` | download | `gateway` | download.clients | 查询可用下载器 |
|
||||
| `GET` | `/api/v1/download/paths` | download | `gateway` | download.paths | 查询可用下载路径 |
|
||||
| `GET` | `/api/v1/download/start/{hashString}` | download | `provider-skill` | downloader-operation | 开始任务 |
|
||||
@@ -310,9 +310,9 @@
|
||||
| `POST` | `/api/v1/subscribe/` | subscribe | `gateway` | subscription.add | 新增订阅 |
|
||||
| `PUT` | `/api/v1/subscribe/` | subscribe | `gateway` | subscription.update | 更新订阅 |
|
||||
| `POST` | `/api/v1/subscribe/check` | subscribe | `gateway` | subscription.metadata.refresh | 刷新订阅 TMDB 信息 |
|
||||
| `GET` | `/api/v1/subscribe/execution/batches` | subscribe | `ui_presentation` | host-ui | 查看订阅搜索进度 |
|
||||
| `GET` | `/api/v1/subscribe/execution/batches/{batch_id}` | subscribe | `ui_presentation` | host-ui | 查看一次订阅搜索 |
|
||||
| `PUT` | `/api/v1/subscribe/execution/batches/{batch_id}/cancel` | subscribe | `ui_presentation` | host-ui | 停止一次订阅搜索 |
|
||||
| `GET` | `/api/v1/subscribe/execution/batches` | subscribe | `gateway` | subscription.execution.list | 查看订阅搜索进度 |
|
||||
| `GET` | `/api/v1/subscribe/execution/batches/{batch_id}` | subscribe | `gateway` | subscription.execution.get | 查看一次订阅搜索 |
|
||||
| `PUT` | `/api/v1/subscribe/execution/batches/{batch_id}/cancel` | subscribe | `gateway` | subscription.execution.cancel | 停止一次订阅搜索 |
|
||||
| `GET` | `/api/v1/subscribe/files/{subscribe_id}` | subscribe | `gateway` | subscription.files | 订阅相关文件信息 |
|
||||
| `DELETE` | `/api/v1/subscribe/follow` | subscribe | `gateway` | subscription.follow.delete | 取消Follow订阅分享人 |
|
||||
| `GET` | `/api/v1/subscribe/follow` | subscribe | `gateway` | subscription.follow.list | 查询已Follow的订阅分享人 |
|
||||
@@ -375,9 +375,9 @@
|
||||
| `POST` | `/api/v1/system/upgrade` | system | `gateway` | system.upgrade.dev | Dev 更新并重启系统 |
|
||||
| `GET` | `/api/v1/system/usage/statistic` | system | `gateway` | system.usage.statistics | 查询安装版本统计报表 |
|
||||
| `GET` | `/api/v1/system/versions` | system | `gateway` | system.versions | 查询Github所有Release版本 |
|
||||
| `DELETE` | `/api/v1/tmdb/cache` | tmdb | `consolidated` | moviepilot-api | 清空 TheMovieDb 识别缓存 |
|
||||
| `GET` | `/api/v1/tmdb/cache` | tmdb | `consolidated` | moviepilot-api | 查询 TheMovieDb 识别缓存 |
|
||||
| `DELETE` | `/api/v1/tmdb/cache/{cache_key}` | tmdb | `consolidated` | moviepilot-api | 删除指定 TheMovieDb 识别缓存 |
|
||||
| `DELETE` | `/api/v1/tmdb/cache` | tmdb | `gateway` | media.cache.clear | 清空 TheMovieDb 识别缓存 |
|
||||
| `GET` | `/api/v1/tmdb/cache` | tmdb | `gateway` | media.cache.get | 查询 TheMovieDb 识别缓存 |
|
||||
| `DELETE` | `/api/v1/tmdb/cache/{cache_key}` | tmdb | `gateway` | media.cache.delete | 删除指定 TheMovieDb 识别缓存 |
|
||||
| `GET` | `/api/v1/tmdb/collection/{collection_id}` | tmdb | `consolidated` | moviepilot-api | 系列合集详情 |
|
||||
| `GET` | `/api/v1/tmdb/credits/{tmdbid}/{type_name}` | tmdb | `consolidated` | moviepilot-api | 演员阵容 |
|
||||
| `GET` | `/api/v1/tmdb/person/credits/{person_id}` | tmdb | `consolidated` | moviepilot-api | 人物参演作品 |
|
||||
|
||||
@@ -13,23 +13,24 @@ allowed-api-operations: >-
|
||||
media.search media.person.search media.person.credits media.recognize media.scrape
|
||||
media.episode_schedule media.detail subscription.add subscription.update subscription.search
|
||||
subscription.list subscription.shares subscription.popular subscription.history
|
||||
subscription.delete download.add download.tasks.active download.clients download.paths
|
||||
download.history.list download.history.delete transfer.history.delete site.update site.list
|
||||
site.userdata site.test site.cookie.update recommendation.list library.exists library.latest
|
||||
storage.settings storage.list transfer.history transfer.file scheduler.list scheduler.run
|
||||
workflow.list workflow.run plugin.installed plugin.market plugin.capabilities plugin.config.get
|
||||
plugin.config.update plugin.source.options plugin.source.install plugin.source.change
|
||||
plugin.reload plugin.install plugin.uninstall slash.list config.identifiers.get
|
||||
config.identifiers.update search.torrents search.results filter.builtin filter.custom
|
||||
filter.groups filter.custom.add filter.custom.update filter.custom.delete filter.group.add
|
||||
filter.group.update filter.group.delete plugin.data config.system.get config.system.update
|
||||
slash.run music.recognize music.explore music.album.get music.album.related music.artist.get
|
||||
music.artist.albums music.artist.related music.cache.get music.cache.delete music.cache.clear
|
||||
system.versions system.update.status system.update.check system.update.download system.restart
|
||||
system.update.install system.upgrade.dev dashboard.media.statistics dashboard.storage
|
||||
dashboard.processes dashboard.system dashboard.downloader scheduler.progress
|
||||
dashboard.transfer.statistics dashboard.cpu dashboard.memory dashboard.network media.sources
|
||||
media.recognize_file media.classification.fields media.classification.policy.get
|
||||
subscription.delete download.add download.artist_collection download.tasks.active
|
||||
download.clients download.paths download.history.list download.history.delete
|
||||
transfer.history.delete site.update site.list site.userdata site.test site.cookie.update
|
||||
recommendation.list library.exists library.latest storage.settings storage.list transfer.history
|
||||
transfer.file scheduler.list scheduler.run workflow.list workflow.run plugin.installed
|
||||
plugin.market plugin.capabilities plugin.config.get plugin.config.update plugin.source.options
|
||||
plugin.source.install plugin.source.change plugin.reload plugin.install plugin.uninstall
|
||||
slash.list config.identifiers.get config.identifiers.update search.torrents search.results
|
||||
filter.builtin filter.custom filter.groups filter.custom.add filter.custom.update
|
||||
filter.custom.delete filter.group.add filter.group.update filter.group.delete plugin.data
|
||||
config.system.get config.system.update slash.run music.recognize music.explore music.album.get
|
||||
music.album.related music.artist.get music.artist.albums music.artist.related music.cache.get
|
||||
music.cache.delete music.cache.clear system.versions system.update.status system.update.check
|
||||
system.update.download system.restart system.update.install system.upgrade.dev
|
||||
dashboard.media.statistics dashboard.storage dashboard.processes dashboard.system
|
||||
dashboard.downloader scheduler.progress dashboard.transfer.statistics dashboard.cpu
|
||||
dashboard.memory dashboard.network media.sources media.recognize_file media.cache.get
|
||||
media.cache.delete media.cache.clear media.classification.fields media.classification.policy.get
|
||||
media.classification.policy.validate media.classification.policy.preview
|
||||
media.classification.policy.impact media.classification.policy.history
|
||||
media.classification.policy.update media.classification.policy.rollback media.episode_groups
|
||||
@@ -40,10 +41,11 @@ allowed-api-operations: >-
|
||||
site.statistic site.mapping site.supporting subscription.get subscription.find
|
||||
subscription.delete_by_media subscription.status.update subscription.reset
|
||||
subscription.search_all subscription.refresh subscription.metadata.refresh
|
||||
subscription.history.delete subscription.user.list subscription.files subscription.share
|
||||
subscription.share.delete subscription.fork subscription.follow.list subscription.follow.add
|
||||
subscription.follow.delete subscription.share.statistics storage.manage storage.mkdir
|
||||
storage.rename storage.delete transfer.queue transfer.queue.delete transfer.name
|
||||
subscription.history.delete subscription.user.list subscription.files
|
||||
subscription.execution.list subscription.execution.get subscription.execution.cancel
|
||||
subscription.share subscription.share.delete subscription.fork subscription.follow.list
|
||||
subscription.follow.add subscription.follow.delete subscription.share.statistics storage.manage
|
||||
storage.mkdir storage.rename storage.delete transfer.queue transfer.queue.delete transfer.name
|
||||
transfer.target_path transfer.manual_history transfer.episode_format.recommend
|
||||
transfer.manual_reviews transfer.manual_review transfer.manual_review.resolve
|
||||
transfer.history.redo transfer.history.redo_batch transfer.history.clear workflow.create
|
||||
|
||||
@@ -11,6 +11,13 @@ Purpose: Submit one torrent to MoviePilot's normal download workflow.
|
||||
- `query`: none
|
||||
- `body`: `allow_unrecognized` (boolean; default `False`): Allow a download when MoviePilot cannot resolve a canonical media identity.; `downloader` (string|null): Configured downloader instance name.; `media_id` (string|null): Source-native media ID. Always pair it with the exact media_source returned by search.; `media_source` (MediaSource|null): Metadata source identifier. Preserve the exact value returned with media_id.; `music_type` (string(recording,album)|null): Music identity level: recording, album, or artist where supported.; `save_path` (string|null): Configured downloader-side save path for the download or subscription.; `torrent_in*` (TorrentInfo): Complete torrent candidate returned by search.results or search.torrents.
|
||||
|
||||
### `download.artist_collection`
|
||||
`POST /api/v1/download/artist-collection`; policy effect: `external_side_effect`.
|
||||
Purpose: Submit one artist-wide music torrent to MoviePilot's collection download workflow.
|
||||
- `path_params`: none
|
||||
- `query`: none
|
||||
- `body`: `artist_id*` (string; minimum length `1`): Source-native artist ID returned by music search or an album detail response.; `artist_name*` (string; minimum length `1`): Artist name preserved as the identity label for an artist-wide music collection.; `downloader` (string|null): Configured downloader instance name.; `media_source*` (MediaSource): Metadata source identifier. Preserve the exact value returned with media_id.; `save_path` (string|null): Configured downloader-side save path for the download or subscription.; `torrent_in*` (TorrentInfo): Complete torrent candidate returned by search.results or search.torrents.
|
||||
|
||||
### `download.clients`
|
||||
`GET /api/v1/download/clients`; policy effect: `safe_read`.
|
||||
Purpose: List enabled downloader instance names and provider types without credentials.
|
||||
|
||||
@@ -30,6 +30,27 @@ other clients.
|
||||
|
||||
## Operations
|
||||
|
||||
### `media.cache.clear`
|
||||
`DELETE /api/v1/tmdb/cache`; policy effect: `destructive_write`.
|
||||
Purpose: Clear the complete administrator-only TMDB recognition cache.
|
||||
- `path_params`: none
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
### `media.cache.delete`
|
||||
`DELETE /api/v1/tmdb/cache/{cache_key}`; policy effect: `destructive_write`.
|
||||
Purpose: Delete one administrator-only TMDB recognition-cache entry by exact key.
|
||||
- `path_params`: `cache_key*` (string): Exact recognition-cache key returned by the corresponding recognition-cache get operation.
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
### `media.cache.get`
|
||||
`GET /api/v1/tmdb/cache`; policy effect: `safe_read`.
|
||||
Purpose: Inspect the administrator-only TMDB recognition cache and summary counts.
|
||||
- `path_params`: none
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
### `media.classification.fields`
|
||||
`GET /api/v1/media/classification/fields`; policy effect: `safe_read`.
|
||||
Purpose: Read the media-classification field catalog and source capabilities.
|
||||
|
||||
@@ -83,7 +83,7 @@ Purpose: Clear the complete administrator-only MusicBrainz recognition cache.
|
||||
### `music.cache.delete`
|
||||
`DELETE /api/v1/music/cache/{cache_key}`; policy effect: `destructive_write`.
|
||||
Purpose: Delete one administrator-only MusicBrainz recognition-cache entry by exact key.
|
||||
- `path_params`: `cache_key*` (string): Exact recognition-cache key returned by music.cache.get.
|
||||
- `path_params`: `cache_key*` (string): Exact recognition-cache key returned by the corresponding recognition-cache get operation.
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
|
||||
@@ -25,6 +25,28 @@ Purpose: Delete accessible subscriptions matching one canonical media identity.
|
||||
- `query`: `media_source*` (MediaSource): Metadata source identifier. Preserve the exact value returned with media_id.; `music_type` (string|null): Music identity level: recording, album, or artist where supported.; `season` (integer|null): Season number used by the media, search, subscription, or transfer operation.
|
||||
- `body`: none
|
||||
|
||||
### `subscription.execution.cancel`
|
||||
`PUT /api/v1/subscribe/execution/batches/{batch_id}/cancel`; policy effect: `external_side_effect`.
|
||||
Purpose: Request cancellation of one durable subscription-search batch before its download side-effect boundary.
|
||||
- `path_params`: `batch_id*` (string): Stable subscription search batch identifier.
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
### `subscription.execution.get`
|
||||
`GET /api/v1/subscribe/execution/batches/{batch_id}`; policy effect: `safe_read`.
|
||||
Purpose: Read one durable subscription-search batch visible to the current user.
|
||||
- `path_params`: `batch_id*` (string): Stable subscription search batch identifier.
|
||||
- `query`: none
|
||||
- `body`: none
|
||||
|
||||
### `subscription.execution.list`
|
||||
`GET /api/v1/subscribe/execution/batches`; policy effect: `safe_read`.
|
||||
Purpose: List recent durable subscription-search batches visible to the current user.
|
||||
- `response`: `data` remains a list and `collection.result_count` reports the returned items. `collection.total_count` is omitted because this endpoint or its upstream source does not expose a total.
|
||||
- `path_params`: none
|
||||
- `query`: `limit` (integer; default `10`): Maximum number of recent subscription-search batches to return.
|
||||
- `body`: none
|
||||
|
||||
### `subscription.files`
|
||||
`GET /api/v1/subscribe/files/{subscribe_id}`; policy effect: `safe_read`.
|
||||
Purpose: Read local library and transfer-file coverage for one accessible subscription.
|
||||
|
||||
@@ -30,12 +30,12 @@ from app.agent.tools.manager import MoviePilotToolsManager
|
||||
|
||||
def test_api_operation_registry_matches_migration_batches() -> None:
|
||||
"""API 操作注册表必须覆盖各迁移批次且每项具有固定路由。"""
|
||||
assert len(API_FIRST_BATCH_OPERATION_SPECS) == 52
|
||||
assert len(API_FIRST_BATCH_OPERATION_SPECS) == 53
|
||||
assert len(API_PARITY_OPERATION_SPECS) == 15
|
||||
assert len(API_MUSIC_OPERATION_SPECS) == 10
|
||||
assert len(API_SYSTEM_OPERATION_SPECS) == 7
|
||||
assert len(API_EXTENDED_OPERATION_SPECS) == 127
|
||||
assert len(API_OPERATION_SPECS) == 211
|
||||
assert len(API_EXTENDED_OPERATION_SPECS) == 133
|
||||
assert len(API_OPERATION_SPECS) == 218
|
||||
assert {spec.operation_id for spec in API_OPERATION_SPECS} == set(API_OPERATION_ROUTES)
|
||||
assert {
|
||||
"download.list",
|
||||
|
||||
@@ -60,6 +60,29 @@ def test_cleanup_confirmation_stays_in_authenticated_management_workflow() -> No
|
||||
assert (disposition, owner, operations) == ("ui_presentation", "host-ui", [])
|
||||
|
||||
|
||||
def test_recent_business_routes_are_exposed_as_stable_gateway_operations() -> None:
|
||||
"""近期高层业务端点必须保持在 moviepilot_api,而不是落入边界归属。"""
|
||||
audit = json.loads(AUDIT_JSON.read_text(encoding="utf-8"))
|
||||
routes = {(item["method"], item["path"]): item for item in audit["operations"]}
|
||||
expected = {
|
||||
("POST", "/api/v1/download/artist-collection"): "download.artist_collection",
|
||||
("GET", "/api/v1/tmdb/cache"): "media.cache.get",
|
||||
("DELETE", "/api/v1/tmdb/cache/{cache_key}"): "media.cache.delete",
|
||||
("DELETE", "/api/v1/tmdb/cache"): "media.cache.clear",
|
||||
("GET", "/api/v1/subscribe/execution/batches"): "subscription.execution.list",
|
||||
("GET", "/api/v1/subscribe/execution/batches/{batch_id}"): "subscription.execution.get",
|
||||
(
|
||||
"PUT",
|
||||
"/api/v1/subscribe/execution/batches/{batch_id}/cancel",
|
||||
): "subscription.execution.cancel",
|
||||
}
|
||||
|
||||
for route, operation_id in expected.items():
|
||||
item = routes[route]
|
||||
assert item["disposition"] == "gateway"
|
||||
assert item["operation_ids"] == [operation_id]
|
||||
|
||||
|
||||
def test_moviepilot_api_skill_routes_contracts_to_category_files() -> None:
|
||||
"""The API Skill entrypoint stays concise and every operation has a namespace file."""
|
||||
entrypoint = API_SKILL.read_text(encoding="utf-8")
|
||||
|
||||
Reference in New Issue
Block a user