Files
UHD_Media_Tool/docs/porting/movieclaw-assessment.md
2026-09-27 18:19:46 +08:00

171 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MovieClaw 可复用资产评估(MIT 边界核对)
> 核对日期 2026-09-27。本文**不是法律意见**,只记录可以复核的事实和保守的工程建议。它补充 `license-and-provenance.md` §1 和 §4 的待办项"MovieClaw 是否有可用的 ≤v0.27.0 MIT tag"。
>
> 源码只读克隆在 `spikes/movieclaw-src/repo`(`spikes/movieclaw-src/.gitignore` 的内容是 `*`,不进 git)。工作区检出在 **`v0.27.0`**。仓库地址是 `https://github.com/movieclaw/movieclaw`,来自 REQ 附录 [S16]。
## 1. 许可时间线(以 LICENSE 文件内容为准)
逐个 tag 读取 `git show <tag>:LICENSE` 和 `pyproject.toml` 的 `version`,再用 `git log --follow -- LICENSE` 核对 LICENSE 的每一次改动。
| 时间(+0800) | 提交 / tag | LICENSE 内容 | 说明 |
| --- | --- | --- | --- |
| 2026-05-08 | `cbc54c1` 首次提交 | MIT,版权人 yipengfei | |
| 2026-07-18 至 08-26 | `torrent-ner-v1` 到 `v0.18.0`,共 21 个 tag | MIT,版权人 yipengfei | 每个 tag 都读过 |
| 2026-08-30 21:00 | `50b43fe` | MIT,版权人改为 MovieClaw | 只改了版权人,许可种类没变 |
| 2026-08-31 至 09-18 | `v0.19.0` 到 `v0.26.0` | MIT,版权人 MovieClaw | 每个 tag 都读过 |
| **2026-09-24** | **`v0.27.0` = `c79bb1b0`**(最后一个 MIT tag) | **MIT**,`Copyright (c) 2026 MovieClaw`,文件 md5 `73a392ea…` | `pyproject` 版本是 `0.27.0`,`license = { text = "MIT" }` |
| 2026-09-26 20:04 | **`403a3547`**(最后一个 MIT 提交,没有 tag) | MIT | 在 v0.27.0 之后又有 14 个提交,改的都是播放、Worker 和 Web 外壳 |
| **2026-09-26 23:55** | **`251f1988`**(许可切换,#457) | **MovieClaw License**,即 Apache-2.0 加非商业附加条件 | 这次提交还改了 README、`pyproject` 的 license 字段和 `movieclaw_jellyfin/authhdr.py`。`version` 仍是 `0.27.0` |
| 2026-09-27 03:34 | `fc9eb724`(REQ 附录快照) | 附条件许可 | **本项目的 ledger CL-009 和 CL-010 目前锚定在这个提交上,它属于新许可** |
| 2026-09-27 | `main` = `eb467dcb` | 附条件许可 | 从 v0.27.0 到 main,共改动 163 个文件,新增 15111 行,删除 2892 行 |
**结论。** 应当以 tag `v0.27.0`(`c79bb1b0`)作为 MIT 基线。`403a354` 也带 MIT LICENSE,但新许可第五条写的是"v0.27.0 及之前**发布的版本**"。没有打 tag 的提交是否算"发布"存在争议,所以不用它。另外,本文关心的所有文件在 `v0.27.0..403a354` 之间都没有变化(见 §2.3),放弃这个提交没有任何损失。
## 2. 站点定义
### 2.1 v0.27.0 中 `src/movieclaw_tracker/sites/configs/` 的全部文件(24 个)
`_template.yaml` 以及下面 23 个站点:
| site_id | 名称 | 框架 | 分页偏移 | H&R 选择器 | 促销规则 | 促销截止时间 | 覆盖 login_* | 覆盖字段数 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **audiences** | 观众 | nexusphp | -1 | — | 默认 `img.pro_*` | — | — | 8 |
| **chdbits** | 彩虹岛 | nexusphp | -1 | — | 默认 | 有 | 有 | 14 |
| **hhanclub** | 憨憨 | nexusphp(自定义 div 模板) | -1 | 有 | 自定义 | — | 有 | 33 |
| **mteam** | M-Team | api,另有 `custom/mteam.py` | 0 | —(API 不提供) | — | — | — | 0 |
| agsvpt | AGSVPT | nexusphp | -1 | — | 默认 | — | — | 12 |
| hdarea | HDArea | nexusphp | -1 | — | 自定义 | — | — | 27 |
| hddolby | HDDolby | nexusphp | -1 | — | 自定义 | — | — | 27 |
| hdfans | HDFans | nexusphp | -1 | — | 自定义 | — | — | 27 |
| hdhome | 高清家园 | nexusphp | -1 | 有 | 默认 | — | — | 14 |
| hdsky | HDSky | nexusphp | -1 | — | 自定义 | — | 有 | 32 |
| hdtime | HDTime | nexusphp | -1 | — | 自定义 | — | — | 27 |
| hdvideo | HDVIDEO | nexusphp | -1 | — | 自定义 | — | 有 | 32 |
| keepfrds | 朋友 | nexusphp | -1 | — | 自定义 | — | — | 11 |
| nicept | NicePT | nexusphp | -1 | 有 | 自定义 | — | — | 27 |
| ourbits | OurBits | nexusphp,另有 `custom/ourbits.py`(`cat[]` 参数) | -1 | 有 | 默认 | — | — | 12 |
| piggo | 猪猪 | nexusphp | -1 | — | 自定义 | — | — | 27 |
| pter | 猫站 | nexusphp | -1 | — | 默认 | — | — | 12 |
| pthome | 铂金家 | nexusphp | -1 | — | 默认 | — | — | 13 |
| pttime | PTTime | nexusphp | -1 | — | 自定义 | — | — | 27 |
| soulvoice | 聆音 | nexusphp | -1 | — | 自定义 | — | — | 27 |
| ssd | 不可说 | nexusphp | -1 | — | 自定义 | 有 | 有 | 25 |
| tjupt | 北洋园 | nexusphp | 0 | — | 默认 | — | 有 | 26 |
| ttg | 听听歌 | nexusphp,另有 `custom/ttg.py` | 0 | — | 自定义 | 有 | 有 | 13 |
没有任何一个站点配置了 `hr_seed_hours`,它只在模板里出现过。
### 2.2 四个重点站点的字段结构(v0.27.0)
YAML 的顶层键包括 `site_id`、`display_name`、`base_url`、`framework`,以及可选的 `web_base_url`、`auth.supported`、`min_request_interval`、`timezone`、`custom_class`、`selectors`、`categories`。`selectors` 只写需要覆盖的键,没写的键沿用 `movieclaw_tracker/selectors.py` 中 `NexusPHPSelectors` 的默认值。
- **audiences**:`base_url` 是 `https://audiences.me`。行选择器是 `table.torrents > tr:has(table.torrentname)`。分类取自 `a[href^='?cat=']::attr(href)`,副标题是 `span.torrent-subtitle-line`,`page_offset` 为 -1。用户资料里,上传量和下载量取自 userbar 的 `span.site-userbar__compact-metric--uploaded`,下载同理。分类映射为 movie 401、tv 402/403、documentary 406。**这些选择器只覆盖了列表和用户资料**:促销、H&R、详情页都沿用默认值,没有 search 相关的参数。
- **hhanclub**:`base_url` 是 `https://hhanclub.net`。列表是 div 卡片结构,行选择器为 `div.torrent-table-sub-info`,标题、副标题、大小、做种、下载、完成、发布者、时间(带 `span[title]` 回退)、详情和下载链接都有专用的 `div.torrent-info-text-*` 选择器。促销使用 `span.promotion-tag-*`,新旧两套类名共 8 条下载系数规则、4 条上传系数规则。H&R 用 `div.torrent-info-hr` 识别。用户资料取自顶栏,有 10 项,其中很多依赖 `::next_sibling_text`。详情页是 Tailwind 的 div 结构,用 `:contains('标题')` 定位。分类有 6 组,另外在注释里写了 401 到 412 的完整分类表。
- **chdbits**:`base_url` 是 `https://ptchdbits.co`(注释写明 2026-08-01 从 chdbits.co 换了域名)。行选择器与 audiences 相同,副标题是 `font.subtitle`。促销沿用默认的 `img.pro_*`,截止时间取 `td.embedded > span[title]`。登录成功的判定是 `a[href='logout.php']`。用户资料取自顶栏的 `font.color_*` 加 `::next_sibling_text`,VIP 组用 `a[class^='VIP']` 识别。分类有 6 组。
- **mteam**:`base_url` 是 `https://api.m-team.cc`,`web_base_url` 是 `https://tp.m-team.cc`,`framework: api`,`auth.supported: [apikey]`,`min_request_interval` 为 1.0。分类表有 8 组,把成人内容的 14 个 ID 单独列出。接口逻辑在 `sites/custom/mteam.py`(509 行),用到 `/api/torrent/search`、`/api/torrent/detail`、`/api/torrent/genDlToken`、`/api/member/profile`、`/api/tracker/myPeerStatus`,还有 `mode=adult` 独立索引和 mallSingleFree 覆盖。
### 2.3 与 main 的差异
`git diff --stat v0.27.0 main -- src/movieclaw_tracker/` 的输出为**空**:整个 tracker 包(站点配置、`custom/*.py`、`frameworks/nexusphp.py`、`selectors.py`、`auth.py`、`http.py`、`ratelimit.py`、`registry.py`)在 main 上**没有任何新增或修改**。站点配置目录最后一次改动是 `ff5a839`(2026-09-05,OurBits 的 `cat[]`)。所以就站点定义而言,main 上不存在受新许可约束的增量,v0.27.0 的版本就是最新版本。
## 3. 其他资产分类
结论同上。对下列路径执行 `git diff --stat v0.27.0 main`,结果**都为空**:`src/movieclaw_downloader`、`src/movieclaw_agent`、`apps/web/lib/{task-activity,job-attention,agent-activity,agent-media-cards}.ts`、`apps/web/components/{agent-conversation-view,task-center-view,agent-media-cards}.tsx`、`src/movieclaw_api/services/{download_health,diagnosis_handoff}.py`、`docs/design/agent-skills.md`。
### 3.1 可以直接移植(MIT,取 v0.27.0)
| 资产(v0.27.0 路径) | 规模 | 建议落点 | 备注 |
| --- | --- | --- | --- |
| `src/movieclaw_tracker/sites/configs/*.yaml`(23 个站点) | 36–95 行/个 | `packages/adapters/src/sites/definitions/<id>.json`,并在 `registry.ts` 登记 | 需要转换 schema,映射见 §4。选择器是 MC 作者实测的结果,**仍须用我们自己的账号核验** |
| `sites/custom/mteam.py` | 509 | 对照 `packages/adapters/src/sites/mteam/*` 补齐 detail、profile、`myPeerStatus`、adult 索引、mallSingleFree | 现有实现是按 MP 做 behavior-ref,可以把 MC 的 detail 字段直接作为依据 |
| `sites/custom/ourbits.py`、`ttg.py` | 41、302 | `packages/adapters/src/sites/<id>/` | TTG 不是标准 NexusPHP 结构,要另写 spider |
| `frameworks/nexusphp.py`、`selectors.py` | 518、169 | `spider/*` 的补充:`::next_sibling_text`、`:contains`、促销截止时间正则、用户资料解析(M2) | 本项目的 spider 已按 MP 实现,这里只借用 MP 没有的能力 |
| `auth.py`(cookie、账号密码模拟登录、apikey) | 506 | M2 站点登录 | 账号密码登录涉及站点规则,按需使用 |
| `src/movieclaw_downloader/clients/transmission.py`、`base.py`、`models.py`、`torrent.py` | 493、151、206、112 | `packages/adapters/src/transmission/*`(新增) | 本项目目前只有 qB |
| `src/movieclaw_downloader/clients/qbittorrent.py` | 543 | 对照 `packages/adapters/src/qbittorrent/*` | 可以补 `set_file_selection`、限速、`set_location` |
| `src/movieclaw_api/services/download_health.py`、`diagnosis_handoff.py` | 298、657 | `packages/tasks` 或 `packages/agent` | REQ §13.4 提到的根因聚合和诊断上下文。关联键要加入下载器实例 ID(REQ §13.4 末段) |
| `apps/web/lib/task-activity.ts`、`job-attention.ts`、`agent-activity.ts`、`agent-media-cards.ts` | 178、43、34、135 | `apps/web/src/lib/*` | 纯逻辑,改动最小 |
| `apps/web/components/agent-conversation-view.tsx`、`task-center-view.tsx`、`agent-media-cards.tsx` | 872、2345、518 | `apps/web` | 同样是 React,但依赖 Next 和 MC 的 `@/components/*`、`@/lib/api/*`,只能取局部。建议只移植交互结构,样式按本项目的设计系统重写 |
| `docs/design/agent-skills.md` 以及 `src/movieclaw_agent/skills.py` | — | `packages/agent` | Markdown Skills 的加载和覆盖机制。REQ §606 已经决定不照搬"同名覆盖立即生效" |
### 3.2 不需要,或者不能按 MC 的许可处理
| 资产 | 原因 |
| --- | --- |
| `src/movieclaw_agent/builtin-skills/skill-creator/*` | 附带自己的 `LICENSE.txt`(Apache-2.0,第三方组件),**不属于 MC 的 MIT 范围**。需要时去上游取,并遵守 Apache-2.0 |
| `apps/web/vendor/liquid-glass/*` | 第三方 MIT,版权人是 "Liquid Glass OSS contributors",与我们的需求无关 |
| RSS 处理 | MC 在 v0.27.0 **没有独立的 RSS 解析模块**,只在 `movieclaw_db/models/site_torrent.py` 定义了 `RSS` 来源枚举和"RSS 观测不写易变层"的规则。这条设计思路可以参考,没有代码可移植 |
| 通用 NexusPHP 解析器 | 见 §3.1,可以移植。本项目已有基于 MP 的 spider,不必替换 |
### 3.3 只能参考(新许可,v0.27.0 之后)
main 上新增的内容集中在 `macos/MovieClawTranscoder`(Mac 转码 Worker)、播放和 Jellyfin 兼容(`movieclaw_playback`、`movieclaw_jellyfin`)、Web 外壳主题、订阅规则组适用范围(#446)、发布说明写作指南,以及 `docs/design/*` 的新文档。这些都**不在本项目首期范围内**。如果需要参考,只读设计思路,不复制代码,并按 `data-ref`/`behavior-ref` 登记,提交号写到具体提交,不写 `main`。
## 4. YAML 到本项目 schema 的字段映射
本项目的 schema 定义在 `packages/adapters/src/sites/spider/schema.ts`(MP indexer schema 加上 `umt` 扩展)。MC 的选择器是 parsel/cssselect 语法。
| MC 键 | 本项目键 | 转换规则 |
| --- | --- | --- |
| `site_id` / `display_name` | `id` / `name` 和 `umt.displayName` | 原样 |
| `base_url` | `domain` | 补上末尾的 `/` |
| `web_base_url` | 馒头使用 `mteam/definition.ts` | 仅用于 API 型站点 |
| `framework: nexusphp` 或 `api` 加 `custom_class` | `umt.kind` / `parser` | 自定义类要单独写 TS 适配器 |
| `min_request_interval`(秒) | `umt.rateLimit.minIntervalMs` | ×1000。不配置时 MC 默认 3 秒,与我们一致 |
| `timezone`(IANA) | `umt.timezone` | `Asia/Shanghai` 转为 `+08:00` |
| `auth.supported` | 凭据配置(SecretResolver) | 只取信息:该站支持 cookie、账号密码还是 apikey |
| `selectors.search_path`、`search_keyword_param` | `search.paths[].path`、`search.params` | 默认是 `torrents.php` 和 `search`。MC 没有配置 `search_area` |
| `selectors.page_offset` | `browse.start` | `-1` 对应 `start: 0`,`0` 对应 `start: 1` |
| `torrent_row_css` | `torrents.list.selector` | 本项目用 htmlparser2 解析,不插入 tbody,可以直接使用 |
| `torrent_title_css` | `fields.title_default` | |
| `torrent_subtitle_css` | `fields.description` | |
| `torrent_category_css`(`X::attr(href)`) | `fields.category` = `{selector:X, attribute:"href", filters:[querystring cat]}` | 通用规则:`X::attr(a)` 转为 `{selector:X, attribute:a}`;`X::text` 转为 `{selector:X}` |
| `torrent_size/seeders/leechers/snatched_css` | `size/seeders/leechers/grabs` | |
| `torrent_time_css` / `torrent_time_fallback_css` | `date_added`(带 attribute title)/ `date_elapsed` | 复用现有的 `date` 模板 |
| `torrent_uploader_css` | 无对应字段 | 可以不取,或放进 raw |
| `promo_download_rules` / `promo_upload_rules` | `downloadvolumefactor.case` / `uploadvolumefactor.case` | 保持声明顺序,末尾补 `"*": 1`。本项目用浮点,没有 MP 的 int 问题 |
| `torrent_promo_deadline_css` / `_attr` / `_re` | `fields.freedate` 的 `{selector, attribute, filters:[re_search]}` | |
| `torrent_hr_css` | `fields.hr` | 没配置时保持 `unknown`,与 MC 的"未知"语义一致 |
| `hr_seed_hours` | `umt` 需要新增,例如 `umt.hrSeedHours` | 所有站点都没配,暂不实现 |
| `torrent_detail_url_css` / `torrent_download_url_css` | `details` / `download` | |
| `login_success_css` | `umt.loginSuccess` | |
| `detail_*_css` | `umt.detail.*` | 目前 `umt.detail` 是 rowhead 表格模型,**HHClub 的 div 详情页放不进去**,需要扩展成逐字段选择器 |
| `profile_*_css` | M2 用户数据 schema(新增) | 需要支持 `::next_sibling_text`(取紧随其后的文本节点)和 `:contains()` |
| `categories.{movie,tv,...}` | `category.movie/tv/music[{id}]` 与 `umt.categoryNames` | MP 只区分 movie、tv、music 三类,documentary、anime、game、av、other 只进 `categoryNames`,保留原始 ID(对应 README 中的偏离项 1) |
| MC 的分类请求参数 `cat<id>=1` | `SpiderCategoryItem`(缺省行为) | OurBits 需要 `param: "cat[]"` |
**与现有 `audiences.json` 的差异。** 下面这些点用 MC 的值去核对:分类选择器 MC 用 `^=`,我们用 `*=`;标题 MC 取链接文本,我们优先取 `title` 属性;`search_area=4` 在 MC 里没有出现;H&R 两边都没有配置;用户资料用的是 userbar 的 compact-metric 类。MC 的定义同样没有覆盖 `td:nth-child` 列号,沿用的是默认的 5 到 8 列,与我们的配置一致。
## 5. 新许可条款转述(`main:LICENSE`,299 行,以中文版为准)
1. **结构**:以 Apache-2.0 全文为基础,另加附加条件。两者冲突时,附加条件优先。适用中华人民共和国法律。
2. **免费使用范围**:只限非商业用途,并且可以为这些用途修改。非商业用途包括:(1) 个人或家庭自用,包括部署在自己的设备或自己租用的服务器上;(2) 在家庭成员和亲友范围内非营利地共享;(3) 学习、研究和教学。
3. **需要书面授权的商业用途**:(1) 以 SaaS、托管、多租户等形式向第三方提供,**不论是否收费**;(2) 出售、出租,或者预装、捆绑到收费的硬件(包括 NAS)或服务中;(3) 收费部署、代运维,或者作为付费会员、收费社群的权益;(4) **公司或营利组织在经营活动中使用,包括内部使用**;(5) 其他以直接或间接获取经济利益为目的的使用。
4. **再分发**:只允许非商业分发。分发时要附上许可全文并保留各项声明,修改版本要显著注明修改内容。**衍生作品必须继续适用本许可**,不得改用其他许可,也不得削弱附加条件。
5. **历史版本**:v0.27.0 及之前发布的版本仍适用 MIT。
6. **贡献**:贡献者同意版权人可以更改许可,也可以把贡献用于商业授权。
"内部使用"是否受限,取决于使用主体:个人或家庭自托管明确免费;公司或营利组织的内部使用明确需要授权。
**保守建议**:本项目的使用主体是谁(个人、家庭,还是公司或营利组织),应由用户自己确认。在确认之前,对 v0.27.0 之后的内容一律只读设计思路。按目前的核对结果,本项目需要的资产都已在 v0.27.0 中,没有必要触碰新许可下的内容。
## 6. 风险与建议
1. **ledger 需要改锚点**:`license-and-provenance.md` 中 CL-009 和 CL-010 锚定的 `fc9eb72` 在许可切换之后。建议追加新行,锚定到 `v0.27.0`(`c79bb1b0`)、许可 MIT,旧行按 §3.3 的规则标记为 `retired`,不删除。这两个文件在两个提交里的内容完全相同,已用 diff 核实。
2. **现有政策禁止 `copied`**:`license-and-provenance.md` §2 和 §3.3 规定"不应出现 `copied`,出现即 CI 失败"。如果要直接转换 MC 的 YAML 选择器值,需要用户先修改这条政策。建议新增 `kind=copied-mit`:要求 `notice_required=是`,在仓库根目录的 `THIRD_PARTY_NOTICES` 或 `packages/adapters/src/sites/definitions/NOTICE` 中附上 MC v0.27.0 的 MIT 全文和 `Copyright (c) 2026 MovieClaw`(v0.18.0 及之前的版本版权人是 yipengfei,取 v0.27.0 即可);每个 JSON 的 `_provenance` 写明 `movieclaw@c79bb1b0:<路径>`。这份文档的写作范围不包括修改政策文件。
3. **MIT 已经足够宽松**:它允许商业使用、修改和再分发,只要求保留版权和许可声明。MIT 授权不能被追溯撤回,这是通行理解,但不作法律结论。
4. **上游来源的连带风险**:v0.27.0 的 tracker 和 downloader 代码中没有出现 MoviePilot、Jackett、Cardigann 等字样(已 grep 核实)。但无法排除 MC 的作者参考过 GPL 项目。选择器属于站点公开 HTML 结构这类事实数据,受版权保护的程度较低。
5. **选择器的时效**:MC 最后一次修改站点配置是 2026-09-05,其中憨憨的新版类名是 2026-08 实测的。所有站点都要用我们自己的账号核验。观众的 promo 和 H&R 配置在 MC 里也是空的,**MC 并不比我们多知道这部分**。
6. **不要把许可切换之后的代码混进来**:克隆目录固定在 `v0.27.0`(detached HEAD)。移植时只用 `git show v0.27.0:<path>` 取文件,不从 main 取。
7. **第三方子组件**:`skill-creator`(Apache-2.0)和 `liquid-glass`(MIT)有各自的许可证,不属于 MC 自己的 MIT 范围。
### 建议的执行顺序
1. 用户确认第 2 条的政策修改。
2. 先转换 audiences、chdbits、hhanclub 三个站点的 JSON,扩展 `umt.detail` 以支持 HHClub 的 div 结构。
3. 馒头补齐 detail 和 profile。
4. 批量转换其余 NexusPHP 站点(ttg、ourbits 需要自定义适配器)。
5. 新增 Transmission 适配器。
6. 前端移植 task-activity 和 job-attention 的逻辑。