- 删除 docs/plan 下 00–18 的计划、评审、验收、发布记录与升级证据,docs/spikes 与 spikes 验证代码
(保留 spikes/movieclaw-src 本地克隆),以及 4 份一次性审查 / 调研;全部可在 ceb66f8 查到
- MP 对照移到 docs/porting/moviepilot-v3-comparison.md;经验教训改为 docs/plan/lessons.md(补第 25 条)
- docs/plan/README.md:文档放哪、当前状态、待办
- 引用改写:代码注释指向在维护的文档,其余注明已删除与 git 位置;清掉原有 6 个失效链接
- README 开头改为当前状态与入口;CHANGELOG 补齐 0.3.8–0.3.14 与工程变更
Co-Authored-By: Claude Opus 5.5 <[email protected]>
111 lines
16 KiB
Markdown
111 lines
16 KiB
Markdown
# 许可证核对与来源登记(MoviePilot / MovieClaw)
|
||
|
||
> 本文依据 REQ §14.2 和任务书 §1"参考源码"一栏,属于工程合规边界的说明,**不是法律意见**。在对外分发或商业化之前,必须针对具体要发行的产品另行做法律审查(REQ §14.2 末段)。
|
||
|
||
## 1. 核对结论
|
||
|
||
| 项 | MoviePilot | MovieClaw |
|
||
| --- | --- | --- |
|
||
| 仓库 | `https://github.com/jxxghp/MoviePilot`(分支 `v3`) | `https://github.com/movieclaw/movieclaw`(`main`) |
|
||
| 本次核对的提交 | `e6820a83ab05e8a00eb0e14adf0f357faa965c7e`(2026-08-30,`version.py = v3.0.0`)。本地路径 `/opt/moviepilot-plus/upstream/MoviePilot`,是单提交浅克隆 | `fc9eb7248dd9c4f7cd8f3fbd704a357b0b766b90`(REQ 附录 [S16])。**本机没有源码**(`find /opt -maxdepth 3 -iname "*claw*"` 没有结果),是通过 GitHub API 和 raw 取到该提交的 `LICENSE`、`pyproject.toml` 以及站点配置原文(见 `moviepilot-downloader-and-sites.md` §0) |
|
||
| REQ 附录快照 | `33da9ce65d5020a8e087584a3321c1ff353c1921`:本地仓库里没有这个对象,无法逐行比对。**以本表提交为准**,引用行号时必须写明提交 | 与附录一致 |
|
||
| 仓库许可 | 根目录 `LICENSE` 是 **GNU GPL v3** 全文(674 行,md5 `1ebbd3e34237af26da5dc08a4e440464`) | 该提交的 `LICENSE` 是 **修改版 Apache-2.0,附加非商业条件**;LICENSE 第 44-45 行写明"v0.27.0 及之前发布的版本以 MIT 许可证发布……本许可证适用于 v0.27.0 之后发布的版本" |
|
||
| 包元数据 | `pyproject.toml` 没有 license 字段 | `pyproject.toml` 第 7 行 `version = "0.27.0"`,第 11 行 `license = { text = "MovieClaw License (Apache-2.0 with non-commercial conditions)" }` |
|
||
| MovieClaw 附加条件要点 | — | 两者冲突时附加条件优先;免费使用仅限非商业(个人/家庭自用、亲友间非营利共享、学习研究教学);**公司内部使用也算商业使用**,需要书面授权;再分发要附许可全文,衍生版本继续适用本许可。本项目给用户个人和家庭的媒体设施使用,是否落在"非商业"范围内要由用户确认;在确认之前,对 MovieClaw 一律只参考字段结构(`data-ref`) |
|
||
| 附加声明 | `README.md` 第 17 行写"仅用于学习交流使用,请勿在任何国内平台宣传该项目",第 79-80 行免责申明写"不得用于商业用途……不建议对用户认证机制进行规避或修改并公开发布"。这些是 README 里的声明,**不是 LICENSE 的附加条款**。GPLv3 §7 不允许附加"进一步限制",但按 REQ §14.2 的要求,本项目**仍然尊重这些声明**,不把它们解读成可以忽略的内容 | 附加条件写在 LICENSE 正文里,有约束力:商业使用需要书面授权,衍生版本继续适用同样的条款 |
|
||
| 版本字段陷阱 | — | main 的 `pyproject` 版本号仍是 0.27.0,但 LICENSE 已经是附条件版本。**不能根据版本号推定 main 是 MIT**(REQ §14.2 原话)。要按 MIT 使用,只能引用已经核实过的 `v0.27.0` 及更早的 **tag**,而且要逐个文件核对那个 tag 附带的许可 |
|
||
|
||
### 1.1 MP 中的非源码或闭源部分(不得作为参考源)
|
||
|
||
| 组件 | 位置 | 状态 | 处理 |
|
||
| --- | --- | --- | --- |
|
||
| `SitesHelper`(站点认证、流控、站点索引定义) | `app/application/site/sites.cpython-314-x86_64-linux-gnu.so`,已被 `.gitignore:4` 忽略;仓库只有 `sites.pyi` 类型存根 | 闭源二进制,由 `docker/Dockerfile:261-262` 从 `jxxghp/MoviePilot-Resources` 下载 | **不反编译,不参考。** 站点选择器改以 MovieClaw 配置和我们自己抓取的页面为准 |
|
||
| 站点样例配置 | `tests/test_rust_accel.py` 里有少量站点 indexer 样例(公开测试数据) | GPL-3.0 | 只用来理解字段语义,不作为选择器的来源 |
|
||
| 站点定义数据 | `app/application/site/user.sites.v3.bin`,已被 `.gitignore:12` 忽略,内容加密 | 同上 | 不解密,不使用 |
|
||
| Rust 加速 `moviepilot_rust` 0.3.3 | 以 wheel 形式安装在 `.venv`,源码仓库是 `jxxghp/MoviePilot-Rust` | wheel 附带的 LICENSE 是 GPLv3 | 只对照 Python 参考路径(见 `moviepilot-metadata-recognition.md` §0.1) |
|
||
| 内嵌的 `tmdbv3api` | `app/modules/themoviedb/tmdbv3api/*`,**文件头没有许可说明**,仓库里也没有单独的 LICENSE/NOTICE | 来源推测是 AnthonyBloomer/tmdbv3api(MIT),但未核实 | 不使用;本项目直接调 TMDB REST v3 |
|
||
| 第三方运行依赖 | `anitopy~=2.1.1`、`cn2an~=0.5.24`、`pinyin2hanzi~=0.1.1`、`regex`、`zhconv` 等(`pyproject.toml`) | 各有各的许可 | 本项目如需等价的 npm 库,另行选型并单独登记,不因为 MP 用过就默认可用 |
|
||
|
||
---
|
||
|
||
## 2. 本项目的迁移边界
|
||
|
||
任务书 `00-first-version-plan`(已删除,见 git `ceb66f8`) §1 规定:"MoviePilot:GPLv3,**只做行为对照与 TS 重写,不复制文件**"。REQ §14.1 允许"以具体实现为参考迁移业务逻辑"。两条放在一起执行时,按下表办。
|
||
|
||
| 允许 | 不允许 |
|
||
| --- | --- |
|
||
| 阅读上游源码,理解调用链、条件分支、状态机和数据结构,用我们自己的语言写成**行为对照记录**(也就是 `docs/porting/*.md`) | 把 `.py`、`.yaml` 文件或者大段代码原样放进仓库,包括放进注释或测试 fixture |
|
||
| 用本机上游解释器**运行**上游代码,只为得到行为输出,作为 parity 期望值(脚本放在仓库外,或放在 `packages/testkit/scripts/` 并标注"可选、需要本机 MP") | 让 CI 依赖上游代码;把上游模块作为运行时依赖;通过子进程调用 MP |
|
||
| 按对照记录**独立**编写 TS 实现,命名、结构和类型遵循本项目约定 | 逐行机械翻译(包括让 AI 逐行改写)后当作原创。REQ §14.2:跨语言翻译或 AI 改写不会自动让移植成为没有来源的独立原创 |
|
||
| 引用事实性常量:协议字段名(qB WebUI API、M-Team API、TMDB API)、站点公开的 HTML 结构、行业通用词表(`WEB-DL`、`REMUX`、`DDP5.1`) | 照搬上游**整理过的数据表**:`RELEASE_GROUPS` 站点到组名的正则表(`app/domain/meta/releasegroup.py:26`)、`StreamingPlatforms` 平台码表(`app/domain/meta/streamingplatform.py`,约 270 条)、`_name_nostring_re` 干扰词表。这类表要**自行编制**,并在 ledger 里写明来源(例如公开的 scene/P2P 命名规范、站点官方规则页) |
|
||
| 上游测试里的**标题字符串**(事实数据)可以作为输入语料 | 整份拷贝 `tests/cases/meta.py` 这类测试文件;期望值必须由我们自己按 REQ 判定,不是照抄上游的 target |
|
||
| MovieClaw:阅读 YAML 的**字段结构**,参考其字段设计 | 在没有书面授权时拷贝 MovieClaw main 的配置或代码(附条件许可);把 main 的内容当作 MIT 使用 |
|
||
| MovieClaw **tag `v0.27.0`(`c79bb1b0`,MIT)** 的资产可以原样复制或转换(`kind=copied-mit`),前提是保留 MIT 全文与 `Copyright (c) 2026 MovieClaw`(`packages/adapters/src/sites/definitions/NOTICE-MovieClaw.md`),每个产物写明来源提交与路径(站点定义 JSON 的 `provenance` 字段),且只经 `git show v0.27.0:<path>` 读取(用户 2026-09-27 确认,依据 `movieclaw-assessment.md` §1) | 复制 `v0.27.0` 之后(`251f1988` 起附条件许可)的任何内容;复制 MC 仓库内自带独立许可的第三方子组件(`skill-creator` Apache-2.0、`liquid-glass`)而不遵守其各自许可 |
|
||
|
||
**对每个 PR 的要求**:只要实现参考了上游,PR 描述里就必须写"参考:`docs/porting/<文件>#<节>`",并在 §3 ledger 里补一行,登记状态为 `behavior-ref`。只有真的复制了代码或数据时,才登记为 `copied`;按目前的边界,**不应出现 `copied`**。例外是 `copied-mit`:复制 MIT 许可的资产(目前仅 MovieClaw v0.27.0),必须 `notice_required=是` 并附 NOTICE。
|
||
|
||
### 2.1 分发时的注意事项(首期不分发,先写在这里)
|
||
|
||
- 本项目用于内部部署(REQ §1.1.1)。内部使用不是忽略上游条款的理由(REQ §14.2)。
|
||
- 如果以后出现 `copied` 条目:MP 来源的部分会让整个分发物受 GPLv3 约束(需要同许可、提供源码、保留声明);MovieClaw main 来源的部分需要书面授权。发现这种情况要**先移除,再讨论**。
|
||
- Docker 镜像不得包含 MP 或 MovieClaw 的任何文件,也不得在构建时下载 `MoviePilot-Resources`。
|
||
|
||
---
|
||
|
||
## 3. Component ledger
|
||
|
||
### 3.1 字段模板
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `id` | `CL-<序号>` |
|
||
| `our_module` | 本项目的文件或函数,例如 `packages/domain/src/title/parse.ts#parseReleaseTitle` |
|
||
| `kind` | `behavior-ref`(只参考行为)/ `data-ref`(参考了数据结构,内容自行编制)/ `copied`(复制,原则上禁止)/ `copied-mit`(复制 MIT 资产,须保留 NOTICE,见 §2)/ `dependency`(npm 依赖) |
|
||
| `upstream` | 仓库 URL |
|
||
| `commit_or_version` | 精确提交或 tag,不能写 `main` |
|
||
| `upstream_paths` | 文件:函数:行号 |
|
||
| `license` | 上游在该提交下的许可 |
|
||
| `porting_doc` | `docs/porting/*.md#节` |
|
||
| `modifications` | 和上游行为的差异摘要,要引用对照文档里的"调整项" |
|
||
| `notice_required` | 是 / 否(`copied` 或 MIT/Apache 依赖要保留版权声明) |
|
||
| `distribution` | internal-only / image / source |
|
||
| `reviewer`、`date` | 登记人和日期 |
|
||
|
||
### 3.2 初始登记(W0-C)
|
||
|
||
| id | our_module(建议) | kind | upstream | commit | upstream_paths | license | porting_doc | modifications | notice | distribution |
|
||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||
| CL-001 | `packages/domain/src/title/parse.ts` | behavior-ref | MoviePilot | e6820a8 | `app/domain/metainfo.py:495,533`;`app/domain/meta/metavideo.py:163-874`;`app/domain/meta/metabase.py:244-431,719` | GPL-3.0 | metadata-recognition §2-§3 | 新增 airDate、issue、cut、packHint、未知发布组兜底;调整了 merge 条件 | 否 | internal-only |
|
||
| CL-002 | `packages/domain/src/title/custom-words.ts`、`episode-format.ts` | behavior-ref | MoviePilot | e6820a8 | `app/domain/meta/words.py:54-221`;`app/application/formatting.py:84-230` | GPL-3.0 | metadata-recognition §4 | 禁用 eval,规则版本化,偏移范围收窄 | 否 | internal-only |
|
||
| CL-003 | `packages/domain/src/title/release-groups.ts` | data-ref | MoviePilot | e6820a8 | `app/domain/meta/releasegroup.py:26,117,127` | GPL-3.0 | metadata-recognition §5 | 只参考"站点到组名"的结构和界定符语义,组名表自行编制 | 否 | internal-only |
|
||
| CL-004 | `packages/domain/src/identity/tmdb-match.ts`、`packages/adapters/src/tmdb/*` | behavior-ref | MoviePilot | e6820a8 | `app/modules/themoviedb/__init__.py:190-930`;`app/modules/themoviedb/tmdbapi.py:130-770,961-1045`;`cache.py:143-265` | GPL-3.0 | metadata-recognition §7-§8 | 候选打分,人工锁定,缓存放 PG | 否 | internal-only |
|
||
| CL-005 | `packages/adapters/src/nfo/writer.ts`、`packages/tasks/src/refresh-metadata.ts` | behavior-ref | MoviePilot | e6820a8 | `app/chain/scraping.py:148-395`;`app/modules/themoviedb/scraper.py:35-420` | GPL-3.0 | metadata-recognition §10 | 刮削改为独立任务;uniqueid 里写本地 UUID | 否 | internal-only |
|
||
| CL-006 | `packages/domain/src/organize/*`、`packages/adapters/src/fs/*`、`packages/tasks/src/verify-and-import.ts` | behavior-ref | MoviePilot | e6820a8 | 见 `moviepilot-transfer.md` §2 各表 | GPL-3.0 | transfer §2-§4 | 见 transfer §3 的必须偏离表 | 否 | internal-only |
|
||
| CL-007 | `packages/adapters/src/qbittorrent/*` | behavior-ref | MoviePilot | e6820a8 | `app/modules/qbittorrent/__init__.py`、`qbittorrent.py`(行号见 downloader-and-sites 文档) | GPL-3.0 | downloader-and-sites §qB | 标签接管,删数据前做引用检查 | 否 | internal-only |
|
||
| CL-008 | `packages/adapters/src/sites/nexusphp/*` | behavior-ref | MoviePilot | e6820a8 | `app/modules/indexer/spider/*`、`app/modules/indexer/parser/nexus_php.py`、`nexus_audiences.py` | GPL-3.0 | downloader-and-sites §站点 | 站点定义自行编制;不使用闭源 SitesHelper | 否 | internal-only |
|
||
| CL-009(retired → CL-012) | `packages/adapters/src/sites/nexusphp/configs/audiences.ts` | data-ref | MovieClaw | fc9eb72 | `src/movieclaw_tracker/sites/configs/audiences.yaml` | MovieClaw License(Apache-2.0 加非商业条件) | downloader-and-sites §MovieClaw | **只参考字段结构**;选择器值以我们自己抓取的观众页面为准重新编写 | 否(未复制) | internal-only |
|
||
| CL-010(retired → CL-013) | `packages/adapters/src/sites/mteam/*` | behavior-ref + data-ref | MovieClaw + MoviePilot | fc9eb72 / e6820a8 | MC:`sites/configs/mteam.yaml`、`sites/custom/mteam.py`;MP:`app/modules/indexer/parser/mtorrent.py` | MC 附条件 / GPL-3.0 | downloader-and-sites §M-Team | API 字段以 M-Team 公开 API 为准,按 API Key 模式实现 | 否 | internal-only |
|
||
| CL-011 | `packages/testkit/fixtures/title-parity.json` | data-ref | MoviePilot | e6820a8 | 输入语料参考 `tests/cases/meta.py`;**期望值自行判定** | GPL-3.0(原测试文件) | metadata-recognition §9 | 只取标题字符串,必要时脱敏 | 否 | internal-only |
|
||
| CL-012 | `packages/adapters/src/sites/definitions/*.json`(23 站)、`definitions/manifest.generated.ts`、`spider/mc-selector.ts`、`scripts/mp-sync/movieclaw-import.mjs` | copied-mit | MovieClaw | **v0.27.0**(`c79bb1b0`) | `src/movieclaw_tracker/sites/configs/*.yaml`;`selectors.py` `NexusPHPSelectors` 默认值;`frameworks/nexusphp.py` `_css_text` | MIT | downloader-and-sites §W1-3c | YAML 转为 MP indexer schema + `umt`;观众/馒头/HHClub/CHD 由 `scripts/mp-sync/movieclaw-overrides/*.json` 覆盖;除 4 主力站外 `enabled:false` | 是(`definitions/NOTICE-MovieClaw.md`) | internal-only |
|
||
| CL-013 | `packages/adapters/src/sites/mteam/*` | behavior-ref + copied-mit | MovieClaw + MoviePilot | **v0.27.0**(`c79bb1b0`)/ e6820a8 | MC:`sites/configs/mteam.yaml`(→ `definitions/mteam.json`)、`sites/custom/mteam.py`(字段对照);MP:`app/modules/indexer/spider/mtorrent.py`、`parser/mtorrent.py` | MIT / GPL-3.0 | downloader-and-sites §M-Team | 定义由 MC 转换 + W1-3b 值覆盖;API 行为按 MP | 是(MC 部分) | internal-only |
|
||
|
||
> 新增 npm 依赖(例如 anitopy 的等价实现、`re2`、`fast-xml-parser`)按 `kind=dependency` 登记,填锁定版本和 SPDX。
|
||
|
||
### 3.3 ledger 的维护规则
|
||
|
||
1. ledger 以本文件 §3.2 为唯一登记处,按 id 追加,不删除旧行;撤销时把行标记为 `retired`。
|
||
2. 上游提交升级时,**新增一行**,写新的提交号,不覆盖旧行。对照文档里的行号也要按新提交重新核对。
|
||
3. `copied` 行出现时,CI 必须失败。建议在 `scripts/` 里加一个检查:ledger 中 `kind=copied` 的行数必须为 0。`copied-mit` 不在此列,但必须有 NOTICE 文件且 `notice=是`。
|
||
4. 审核人检查三件事:PR 里有没有"参考:"链接;ledger 有没有对应行;diff 中有没有大段和上游高度相似的代码(可以用 `jscpd` 这类工具,把上游目录作为外部语料做一次抽样检查)。
|
||
|
||
---
|
||
|
||
## 4. 待办与未决
|
||
|
||
| 项 | 状态 |
|
||
| --- | --- |
|
||
| REQ 附录快照 `33da9ce` 和本地 `e6820a8` 的差异 | 未比对,本地没有该对象。需要时 `git fetch --depth=1 origin 33da9ce...` 再比对(需要网络) |
|
||
| MovieClaw 是否有可用的 `≤v0.27.0` MIT tag,以及 tag 内的文件许可 | 未核对。只有在决定复用 MovieClaw 历史代码时才需要做 |
|
||
| `tmdbv3api` 内嵌副本的来源和许可 | 未核实;本项目不使用它 |
|
||
| 对外分发 | 首期不做;如果要做,先完成法律审查 |
|