news 2026/9/23 9:09:09

MoviePilot Torrent Cache API 全解析:种子缓存查询、刷新、重识别与删除的完整操作指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MoviePilot Torrent Cache API 全解析:种子缓存查询、刷新、重识别与删除的完整操作指南
  • 后端
  • AI Agent
  • MCP 服务
  • AI 技能

【免费下载链接】MoviePilot

NAS媒体库自动化管理工具

项目地址:https://gitcode.com/gh_mirrors/mo/MoviePilot
点击查看免费下载

本文是 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)是订阅匹配的数据底座。系统按配置的订阅站点抓取或订阅最新资源后,将候选种子连同其识别出的媒体信息暂存为缓存,供订阅搜索、匹配与后续下载流程复用。理解它的关键前提是两点:

  1. 缓存来源由订阅模式决定subscribe_moderss时使用 RSS 订阅缓存,为spider时使用爬虫缓存。该逻辑在 种子缓存读取端点 中通过get_api_runtime_config_snapshot().subscribe_mode判定,并分别读取"rss""spider"类型的缓存。
  2. 影视与音乐缓存分离存储:缓存文件按媒体类型拆分为影视与音乐两份独立存储,split_cache_contexts 依据torrent.category是否等于MediaType.MUSIC做拆分,写回时分别落盘(cache_files()返回的文件对)。这意味着对缓存做删除、重识别等写操作时,两类缓存会被分别持久化。

Torrent Cache API 正是围绕这套缓存体系暴露的 5 个受控操作,全部经由moviepilot_api网关按固定路由执行,且每个操作都声明了明确的策略效果(policy effect),供 Agent 判断是否需要用户确认。

操作总览

操作 ID方法路径策略效果用途
torrent.cache.getGET/api/v1/torrent/cachesafe_read查看缓存种子及其识别的媒体身份
torrent.cache.refreshPOST/api/v1/torrent/cache/refreshexternal_side_effect从配置的 RSS / 爬虫源刷新种子缓存
torrent.cache.reidentifyPOST/api/v1/torrent/cache/reidentify/{domain}/{torrent_hash}reversible_write重算或替换单条缓存种子的媒体身份
torrent.cache.deleteDELETE/api/v1/torrent/cache/{domain}/{torrent_hash}destructive_write按站点域名与缓存 hash 删除单条缓存
torrent.cache.clearDELETE/api/v1/torrent/cachedestructive_write清空全部种子缓存

路由表在 app/agent/policy/api.py 中登记,策略效果声明在同文件 L508-L518,并与 API 表面审计文档 中的路由清单一一对应。

逐个操作详解

torrent.cache.get:查看缓存与已识别的媒体身份

  • 方法/路径GET /api/v1/torrent/cache
  • 策略效果safe_read(只读,无需确认)
  • 参数path_paramsquerybody均为空。

该操作返回当前订阅模式下全部缓存的种子上下文及其媒体识别结果。从 端点实现 可以看到,它做了三件事:

  1. 按订阅模式读取缓存(RSS 或爬虫);
  2. 对每条缓存计算 hash 并解析媒体身份(resolve_media_identity);
  3. 聚合返回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()。执行流程如下:

  1. 定位缓存条目:校验domain存在,并按HashUtils.md5(title + description) == torrent_hash找到目标Context
  2. 判定音乐实体:当缓存媒体类型为音乐、元信息为MetaMusic、分类为music、或显式传入音乐数据源/音乐类型时走音乐识别分支。音乐重识别仅允许使用音乐元数据源,且music_type只支持recording/album
  3. 媒体识别:若同时提供media_sourcemedia_id,调用media_chain.async_recognize_media按显式 ID 识别;否则调用async_recognize_by_meta按标题/描述解析(此时media_sourcemedia_id必须同时提供或同时缺省,否则返回"媒体来源和媒体 ID 必须同时提供");
  4. 持久化:识别结果写回media_info后,将合并缓存拆分为影视/音乐两份并分别落盘;
  5. 返回结果:三元组(success, message, data)data包含识别后的media_namemedia_yearmedia_typemedia_sourcemedia_idmusic_type,兼容旧插件的 HTTP 响应结构。

torrent.cache.delete:按域名与 hash 删除单条缓存

  • 方法/路径DELETE /api/v1/torrent/cache/{domain}/{torrent_hash}
  • 策略效果destructive_write(破坏性写入,需要确认且不可恢复)
  • path_paramsdomain*(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_idpath_paramsquerybody四要素,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": {} }

几条硬性规则:

  • 参数归桶:路径占位符(domaintorrent_hash等)放path_params;GET 过滤与控制值放query;POST/PUT/PATCH 的请求模型放body。不要把query字段塞进path_params,也不要发送未声明字段。
  • 成对复用标识media_sourcemedia_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,执行前必须确认。
  • 响应检查:以successexecution_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(分类事实)

自动分类策略评估后的规范化媒体事实。

字段类型说明
extensionsobject扩展提供的额外规范化分类事实
field_sourcesobject规范化分类事实的来源出处
identity*ClassificationIdentityFacts稳定的源端原生媒体身份
media*ClassificationMediaFacts用于分类预览的媒体元数据输入
musicClassificationMusicFacts|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(发布时间可读表示)、descriptiondownloadvolumefactor/uploadvolumefactor(站点下载/上传倍率)、enclosure(下载地址)、freedate/freedate_diff(免费期及剩余秒数)、grabs(完成下载数,默认0)、hit_and_run(是否考核 H&R,默认False)、labelsmedia_id/media_source(成对身份)、page_urlpeers(默认0)、pri_order(索引器优先级,默认0)、pubdateseeders(做种数,默认0)、site/site_cookie(站点 ID 与 cookie,cookie 视为机密)、site_downloadersite_namesite_order(默认0)、site_proxy(默认False)、site_uasize(默认0.0)、titlevolume_factor(综合体积倍率标签)。

WorkflowExecutionConfig

  • max_workers(integer|null):工作流最大并发动作数。

WorkflowExecutionState-Input/WorkflowRuntimeState

持久化可恢复工作流执行状态:errors/nodes/outputs/runtime/version(默认1),以及运行时进度状态attemptsfinished_actions(默认0)、node_statesprogress(默认0)、running_tasks(默认0)等。

实战注意事项

  1. 写操作需确认refreshreidentifydeleteclear均有副作用,Agent 在确认前不得执行;delete/clear属于不可恢复操作。
  2. 重识别失败前置条件:显式提供媒体身份时media_sourcemedia_id缺一不可;音乐重识别只能使用音乐元数据源,music_type仅接受recording/album(artist 仅可浏览)。
  3. hash 复用torrent.cache.get返回的hash可直接用于delete/reidentify的路径参数,无需自行重算;如需自行计算,算法为md5(title + description)
  4. 刷新后校验:执行refresh后用get回读确认缓存规模与媒体识别质量,识别不准确的条目可针对性reidentify
  5. 缓存与站点解耦:清空/删除缓存不会影响站点资源本身,重建缓存依赖后续刷新动作;若需强制全量重建,可先clearrefresh

延伸阅读

  • 完整契约文档: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媒体库自动化管理工具

项目地址:https://gitcode.com/gh_mirrors/mo/MoviePilot
点击查看免费下载

相关推荐

上一篇:探索 GreptimeDB:一款强大的时间序列数据库
下一篇:鸟鸣(Hummingbird):微软开源的轻量级推理引擎

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 9:08:27

micm源码速查手册:3招读懂核心逻辑,告别文档焦虑

micm源码速查手册:3招读懂核心逻辑,告别文档焦虑 官方文档翻了三遍还是云里雾里?别慌,这不是你的问题。很多开发者面对 micm 这种底层组件时,最大的痛点就是文档太长、重点不清晰,看完就忘,写代码时还得反复查。今天这篇 micm 速查手册…

作者头像 李华
网站建设 2026/9/23 9:08:15

职臣AI问卷设计:新手也能搭好研究工具

https://www.zhichenai.com做论文时,问卷并不是“列几个问题、发出去”这么简单。研究主题是否清晰、调查对象是否匹配、题目数量是否合适、选项能否支持后续分析,都会影响最终结论的可信度。对于第一次做问卷的新手来说,最难的往往不是点击生…

作者头像 李华
网站建设 2026/9/23 9:08:14

5道裁决加速器高频面试题,带你从零搞定实战

5道裁决加速器高频面试题,带你从零搞定实战 刚拿到一个线上服务的报错日志,满屏的 StackTrace 像天书一样堆砌,红色的 ERROR 闪烁刺眼,新手往往在这里卡住,连复现路径都找不到。这种“报错一堆看不懂”的无力感,在技术面试中更是高频面试题的重灾区,面试官喜欢拿真实的故障场景考察你的排查逻辑…

作者头像 李华
网站建设 2026/9/23 9:08:11

趣味文字动画开发一文搞懂 Canvas与CSS3实战对比

趣味文字动画开发一文搞懂 Canvas与CSS3实战对比 官方文档里关于字符渲染的章节动辄几百页,参数解释得云里雾里,新手根本抓不住重点。别被那些晦涩的术语吓退,今天我们用大白话把 趣味文字 的底层逻辑拆解清楚。…

作者头像 李华
网站建设 2026/9/23 9:07:54

3个坑搞懂期刊号是什么:图解原理避配置卡死

3个坑搞懂期刊号是什么:图解原理避配置卡死 配置环境就卡半天?别急着骂娘,八成是你没搞清【期刊号是什么】。很多老手都在NPM/PyPI官方包依赖解析上栽过跟头,尤其是那些看起来像乱码的ID。今天不扯虚的,直接上【图解原理】,把这事儿掰开了揉碎了讲清楚。 考点梳理:别再被ID误导了…

作者头像 李华
网站建设 2026/9/23 9:07:50

短剧翻译配音怎么收费?批量译制方案费用对比,附工具推荐

一批中文短剧要做外语版时,AI工具订阅、翻译与配音团队报价、整套成片服务报价往往看起来不在同一张账单里。有人按分钟计费,有人按语种和角色报价,也有人直接报多语种成片项目价。要把短剧翻译配音收费比得公平,不能只看一个“每…

作者头像 李华