- 后端
- AI Agent
- MCP 服务
- AI 技能
【免费下载链接】MoviePilot
NAS媒体库自动化管理工具
本文是 MoviePilot(NAS 媒体库自动化管理工具)中Torrent Cache(种子缓存)API 的技术指南,围绕torrent.cache.*命名空间下的 5 个操作展开:查询(get)、刷新(refresh)、重新识别(reidentify)、单条删除(delete)与全量清理(clear)。它面向两类读者:一类是通过moviepilot_api网关调用该能力的 Agent / LLM 应用开发者,另一类是希望理解种子缓存底层存储与识别机制的 MoviePilot 开发者。读完本文,你将掌握每个操作的 HTTP 路由、参数契约、策略效果(policy effect)与真实调用方式,并能结合源码理解缓存如何按订阅模式(RSS/爬虫)与媒体类型(影视/音乐)独立存储、种子 hash 如何计算、重新识别如何影响下游订阅匹配。
Torrent Cache 是什么:一次说清它的职责边界
在 MoviePilot 中,种子缓存(torrent cache)是订阅匹配的数据底座。系统按配置的订阅站点抓取或订阅最新资源后,将候选种子连同其识别出的媒体信息暂存为缓存,供订阅搜索、匹配与后续下载流程复用。理解它的关键前提是两点:
- 缓存来源由订阅模式决定:
subscribe_mode为rss时使用 RSS 订阅缓存,为spider时使用爬虫缓存。该逻辑在 种子缓存读取端点 中通过get_api_runtime_config_snapshot().subscribe_mode判定,并分别读取"rss"或"spider"类型的缓存。 - 影视与音乐缓存分离存储:缓存文件按媒体类型拆分为影视与音乐两份独立存储,split_cache_contexts 依据
torrent.category是否等于MediaType.MUSIC做拆分,写回时分别落盘(cache_files()返回的文件对)。这意味着对缓存做删除、重识别等写操作时,两类缓存会被分别持久化。
Torrent Cache API 正是围绕这套缓存体系暴露的 5 个受控操作,全部经由moviepilot_api网关按固定路由执行,且每个操作都声明了明确的策略效果(policy effect),供 Agent 判断是否需要用户确认。
操作总览
| 操作 ID | 方法 | 路径 | 策略效果 | 用途 |
|---|---|---|---|---|
torrent.cache.get | GET | /api/v1/torrent/cache | safe_read | 查看缓存种子及其识别的媒体身份 |
torrent.cache.refresh | POST | /api/v1/torrent/cache/refresh | external_side_effect | 从配置的 RSS / 爬虫源刷新种子缓存 |
torrent.cache.reidentify | POST | /api/v1/torrent/cache/reidentify/{domain}/{torrent_hash} | reversible_write | 重算或替换单条缓存种子的媒体身份 |
torrent.cache.delete | DELETE | /api/v1/torrent/cache/{domain}/{torrent_hash} | destructive_write | 按站点域名与缓存 hash 删除单条缓存 |
torrent.cache.clear | DELETE | /api/v1/torrent/cache | destructive_write | 清空全部种子缓存 |
路由表在 app/agent/policy/api.py 中登记,策略效果声明在同文件 L508-L518,并与 API 表面审计文档 中的路由清单一一对应。
逐个操作详解
torrent.cache.get:查看缓存与已识别的媒体身份
- 方法/路径:
GET /api/v1/torrent/cache - 策略效果:
safe_read(只读,无需确认) - 参数:
path_params、query、body均为空。
该操作返回当前订阅模式下全部缓存的种子上下文及其媒体识别结果。从 端点实现 可以看到,它做了三件事:
- 按订阅模式读取缓存(RSS 或爬虫);
- 对每条缓存计算 hash 并解析媒体身份(
resolve_media_identity); - 聚合返回
count(种子总数)、sites(站点数)与data(明细数组)。
每条明细包含的关键字段:
| 字段 | 说明 |
|---|---|
hash | 种子缓存 hash(title + description的 MD5) |
domain | 来源站点域名 |
title/description | 种子标题与描述 |
size/pubdate/site_name | 大小、发布时间、站点名 |
media_name/media_year/media_type | 识别出的媒体名、年份、类型 |
media_source/media_id | 媒体元数据源标识与其原生 ID(两者必须成对使用) |
music_type | 音乐实体类型(recording / album) |
season_episode/resource_term | 季集信息与资源关键词 |
enclosure/page_url | 下载地址与详情页 |
poster_path/backdrop_path | 海报与背景图 |
torrent.cache.refresh:从 RSS / 爬虫源刷新缓存
- 方法/路径:
POST /api/v1/torrent/cache/refresh - 策略效果:
external_side_effect(对外部站点产生副作用,通常需要确认) - 参数:三者均为空。
该操作触发一次全量缓存刷新。底层调用 TorrentsChain.refresh():按订阅模式读取影视/音乐两份缓存 → 过滤无效种子(TorrentHelper().is_invalid校验 enclosure)→ 对配置站点(SystemConfigKey.RssSites或传入站点列表)重新抓取/订阅最新资源。端点返回形如"缓存刷新完成,共刷新 N 个站点,M 个种子"的汇总信息。
torrent.cache.reidentify:重算单条缓存的媒体身份
- 方法/路径:
POST /api/v1/torrent/cache/reidentify/{domain}/{torrent_hash} - 策略效果:
reversible_write(可回滚的写入) - path_params:
domain*(string):站点主机名或域名,用于匹配与请求;torrent_hash*(string):由torrent.cache.get返回的、针对某站点条目的缓存 hash。
- query(均为可选):
media_id(string|null):源端原生媒体 ID,必须与搜索返回的media_source精确配对;media_source(MediaSource|null):元数据源标识,保留与media_id一同返回的精确值;music_type(string(recording,album)|null):音乐身份级别,支持的取值为 recording、album(artist 仅可浏览)。
- body:无。
这是 5 个操作中唯一带业务逻辑的操作,其核心实现在 TorrentCacheRecognitionService.execute()。执行流程如下:
- 定位缓存条目:校验
domain存在,并按HashUtils.md5(title + description) == torrent_hash找到目标Context; - 判定音乐实体:当缓存媒体类型为音乐、元信息为
MetaMusic、分类为music、或显式传入音乐数据源/音乐类型时走音乐识别分支。音乐重识别仅允许使用音乐元数据源,且music_type只支持recording/album; - 媒体识别:若同时提供
media_source与media_id,调用media_chain.async_recognize_media按显式 ID 识别;否则调用async_recognize_by_meta按标题/描述解析(此时media_source、media_id必须同时提供或同时缺省,否则返回"媒体来源和媒体 ID 必须同时提供"); - 持久化:识别结果写回
media_info后,将合并缓存拆分为影视/音乐两份并分别落盘; - 返回结果:三元组
(success, message, data),data包含识别后的media_name、media_year、media_type、media_source、media_id、music_type,兼容旧插件的 HTTP 响应结构。
torrent.cache.delete:按域名与 hash 删除单条缓存
- 方法/路径:
DELETE /api/v1/torrent/cache/{domain}/{torrent_hash} - 策略效果:
destructive_write(破坏性写入,需要确认且不可恢复) - path_params:
domain*(string)、torrent_hash*(string),含义同上。 - query / body:无。
实现要点见 delete_cache 端点:删除前先校验站点存在,再按 hash 过滤掉目标条目;若过滤前后数量不变则返回"未找到指定的种子";删除后同样按影视/音乐拆分回写缓存文件。注意:删除只影响缓存,不影响站点上真实存在的种子,后续refresh可能重新抓取同一资源。
torrent.cache.clear:清空全部种子缓存
- 方法/路径:
DELETE /api/v1/torrent/cache - 策略效果:
destructive_write - 参数:三者均为空。
直接调用 TorrentsChain.async_clear_torrents() 清空缓存。适用场景包括:缓存数据过期、订阅站点配置大改、需要强制全量重建缓存等。清空后建议立即执行torrent.cache.refresh重建。
网关调用约定与策略模型
以上操作不是任意 HTTP 客户端可直接调用的 REST 接口,而是通过moviepilot_api网关暴露的结构化 Agent 操作。调用时只需提供operation_id、path_params、query、body四要素,HTTP 方法与路径、当前用户认证令牌、授权与确认策略均由宿主侧完成。调用模板如下(来源:SKILL.md):
{ "operation_id": "torrent.cache.reidentify", "path_params": {"domain": "example.com", "torrent_hash": "md5-of-title-plus-description"}, "query": {"media_source": "tmdb", "media_id": "12345", "music_type": "album"}, "body": {} }几条硬性规则:
- 参数归桶:路径占位符(
domain、torrent_hash等)放path_params;GET 过滤与控制值放query;POST/PUT/PATCH 的请求模型放body。不要把query字段塞进path_params,也不要发送未声明字段。 - 成对复用标识:
media_source与media_id必须成对出现,并保留搜索/详情返回的精确值;音乐场景还要保留music_type=recording|album|artist(artist 仅可浏览)。 - 策略效果决定确认与恢复:
safe_read(如torrent.cache.get):只读,无需确认,结果敏感度为PRIVATE;external_side_effect(如torrent.cache.refresh):对外部站点产生副作用,须取得确认后执行;reversible_write(如torrent.cache.reidentify):可回滚写入,源码登记了RecoveryMode.RECONCILE恢复模式;destructive_write(如torrent.cache.delete/torrent.cache.clear):破坏性写入,恢复模式为RecoveryMode.NONE,执行前必须确认。
- 响应检查:以
success、execution_outcome、错误信息与空结果为准;unknown结果不得重试写操作,应先用读操作(torrent.cache.get)核实实际状态。 - 超级用户限定:从端点实现可见,所有 Torrent Cache 端点均依赖
get_current_active_superuser(_async)鉴权,仅管理员可操作。
源码级原理:hash 计算与缓存落盘
缓存 hash:title + description的 MD5
无论是删除、重识别还是 get 返回的hash,其算法完全一致:HashUtils.md5(f"{context.torrent_info.title}{context.torrent_info.description}")(见 cache.py 与 torrent.py)。这意味着:
- hash 是确定性的:相同站点的相同种子在多次查询中 hash 不变,可直接缓存复用;
- 修改标题或描述会改变 hash,从而产生"新条目"。
影视/音乐分离持久化
缓存读取通过 async_get_torrents() 完成:分别加载影视缓存文件与音乐缓存文件,做旧版本Context兼容性补齐后合并返回。写操作则统一走"合并 →split_cache_contexts拆分 → 分别async_save_cache"的模式,保证影视与音乐缓存互不污染。
测试验证
仓库中 tests/test_torrent_cache_music.py 覆盖了核心行为,例如:
- 音乐条目在
torrent.cache.get中返回正确的music_type; reidentify_cache在显式指定music_type="album"时,将album命名空间正确转发给识别调用(断言recognize_kwargs["music_type"] == MUSIC_ENTITY_ALBUM);- 自动重识别时保留音乐元信息与实体类型。
这些测试可直接作为理解该 API 行为的可执行文档。
Body Models 参考(Torrent 类别自包含模型)
该类别文档是自包含的:以下共享模型已内联,Agent 无需加载第二个 Skill 文档即可构造调用。字段以*标记必填,括号内为类型与默认值。
ClassificationFacts(分类事实)
自动分类策略评估后的规范化媒体事实。
| 字段 | 类型 | 说明 |
|---|---|---|
extensions | object | 扩展提供的额外规范化分类事实 |
field_sources | object | 规范化分类事实的来源出处 |
identity* | ClassificationIdentityFacts | 稳定的源端原生媒体身份 |
media* | ClassificationMediaFacts | 用于分类预览的媒体元数据输入 |
music | ClassificationMusicFacts|null | 音乐专属规范化事实 |
ClassificationFactsPreviewInput(分类预览输入)
facts*(ClassificationFacts):预览评估所用的规范化媒体事实。kind(string=facts;默认facts):请求选择的分类规则或预览输入类型。
ClassificationIdentityFacts(分类身份事实)
media_id*(string):源端原生媒体 ID,与搜索返回的media_source精确配对。media_source*(string):元数据源标识,保留与media_id一同返回的精确值。
ClassificationMediaFacts(媒体事实)
adult(boolean|null):是否标记为成人内容。companies(array |null):制作公司或工作室。content_rating(string|null):内容分级。countries(array |null):规范化国家/地区代码。genre_keys(array |null):MoviePilot 规范化类型键。genre_names(array |null):源端提供的类型名。language(string|null):规范化语言代码。networks(array |null):电视网或流媒体平台。runtime(integer|null):持久化的工作流运行时元数据(用于安全恢复)。title(string|null):操作使用的媒体/种子/订阅/历史标题。type*(string):所选操作要求的 MoviePilot 媒体或存储条目类型。year(integer|null):用于消除歧义的发行年份。
ClassificationMediaPreviewInput(媒体分类预览输入)
kind(string=media;默认media):分类规则或预览输入类型。media*(object):用于分类预览的媒体元数据输入。
ClassificationMusicFacts(音乐事实)
album_type(string|null):音乐专辑或发行组类型。artist_country(string|null):艺术家所在国家/地区。artists(array |null):音乐艺术家名。entity_type(string|null):音乐实体类型。genres(array |null):规范化音乐流派值。release_status(string|null):音乐发行状态。secondary_types(array |null):次级发行组类型。tags(array |null):逗号分隔的豆瓣音乐分类标签,仅配合豆瓣音乐探索源使用。
ClassificationPolicy-Input(分类策略)
categories(array ):策略中完整的有序媒体分类定义。enrichment_mode(string(primary_only,enrich_missing);默认primary_only):填充分类事实的元数据增强模式。fallbacks(object):无规则匹配时的回退分类或标签动作。field_aliases(object):源专属字段到规范化字段的可选别名映射。mode(string=first_match;默认first_match):操作模式。revision(integer;默认0;最小0.0):已发布策略修订号。rules(array ):按优先级从高到低求值的有序规则。schema_version(integer=2;默认2):服务端期望的策略 schema 版本。updated_at(string|null):对象或执行状态最后更新时间。
FileItem-Input(存储条目)
basename(string|null)、children(array|null)、drive_id(string|null)、extension(string|null)、fileid(string|null)、modify_time(number|null)、name(string|null)、parent_fileid(string|null)、path(string|null;默认/)、pickcode(string|null,115 网盘 pickcode)、size(integer|null,字节)、storage(string|null;默认local)、thumbnail(string|null)、type(string|null)、url(string|null)。
JsonData-Input
任意 JSON 兼容辅助数据,无直接可写字段。
MediaSource/MediaType
规范元数据源标识(与源端原生 ID 配对)与 MoviePilot 媒体类型,均为运行时模型,无直接可写字段。
SubscriptionExecutionStatus(订阅执行状态)
batch_id(string|null):稳定的订阅搜索批次 ID。can_cancel(boolean;默认False):当前执行是否可取消。current_site_id(integer|null):当前处理的站点 ID。error(string|null):可读的错误信息。next_run_at(string|null):下次计划搜索时间。phase*(string):订阅执行的当前阶段。source(string|null):所选元数据或推荐源。state*(string):当前状态过滤。task_id(string|null):稳定的持久化传输任务 ID。updated_at*(string):最后更新时间。
TorrentInfo(种子信息)
MoviePilot 搜索返回的单条种子候选,核心字段包括:category(分类)、date_elapsed(发布时间可读表示)、description、downloadvolumefactor/uploadvolumefactor(站点下载/上传倍率)、enclosure(下载地址)、freedate/freedate_diff(免费期及剩余秒数)、grabs(完成下载数,默认0)、hit_and_run(是否考核 H&R,默认False)、labels、media_id/media_source(成对身份)、page_url、peers(默认0)、pri_order(索引器优先级,默认0)、pubdate、seeders(做种数,默认0)、site/site_cookie(站点 ID 与 cookie,cookie 视为机密)、site_downloader、site_name、site_order(默认0)、site_proxy(默认False)、site_ua、size(默认0.0)、title、volume_factor(综合体积倍率标签)。
WorkflowExecutionConfig
max_workers(integer|null):工作流最大并发动作数。
WorkflowExecutionState-Input/WorkflowRuntimeState
持久化可恢复工作流执行状态:errors/nodes/outputs/runtime/version(默认1),以及运行时进度状态attempts、finished_actions(默认0)、node_states、progress(默认0)、running_tasks(默认0)等。
实战注意事项
- 写操作需确认:
refresh、reidentify、delete、clear均有副作用,Agent 在确认前不得执行;delete/clear属于不可恢复操作。 - 重识别失败前置条件:显式提供媒体身份时
media_source与media_id缺一不可;音乐重识别只能使用音乐元数据源,music_type仅接受recording/album(artist 仅可浏览)。 - hash 复用:
torrent.cache.get返回的hash可直接用于delete/reidentify的路径参数,无需自行重算;如需自行计算,算法为md5(title + description)。 - 刷新后校验:执行
refresh后用get回读确认缓存规模与媒体识别质量,识别不准确的条目可针对性reidentify。 - 缓存与站点解耦:清空/删除缓存不会影响站点资源本身,重建缓存依赖后续刷新动作;若需强制全量重建,可先
clear再refresh。
延伸阅读
- 完整契约文档:skills/moviepilot-api/api/torrent.md
- 调用入口与路由规则:skills/moviepilot-api/SKILL.md
- 网关路由与策略登记:app/agent/policy/api.py
- 端点实现:app/api/endpoints/torrent.py
- 重识别用例服务:app/application/torrent/cache.py
- 缓存读取/拆分/刷新链:app/chain/torrents.py
- 测试用例:tests/test_torrent_cache_music.py
- API 表面审计清单:docs/refactor/agent-api-surface-audit.md
- 后端
- AI Agent
- MCP 服务
- AI 技能
【免费下载链接】MoviePilot
NAS媒体库自动化管理工具
相关推荐
深度解析POCO数据库查询缓存失效:更新与删除操作的影响与优化
深度解析POCO数据库查询缓存失效:更新与删除操作的影响与优化 在构建高性能C++应用程序时, 数据库查询缓存 是提升性能的关键技术。POCO C++ Libr
后端网络/通信数据库密码学Web框架Transmission种子完成后种子清理:自动删除torrent文件
Transmission种子完成后种子清理:自动删除torrent文件 你是否遇到过下载完成后,Transmission仍然保留着大量种子文件占用空间的问题?是
桌面应用后端CLI网络AutoGPT 平台 Airtable Records 记录操作块完整指南:创建、查询、更新与删除
AutoGPT 平台 Airtable Records 记录操作块完整指南:创建、查询、更新与删除 本篇技术指南以 AutoGPT Platform 官方文档
人工智能AI Agent自主智能体Agent 工作流工作流自动化后端前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考