Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions .trellis/tasks/09-28-m1-skeleton-tracer/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@

## 進度與交接(2026-09-30 更新;compact 後從這裡接)

- **已合併進 `main`**:#173(設計文件)、#174(PR 1 指令檔分家)、#175(PR 2 骨架與 CI)、#177(PR 3 lint)、#178(PR 4 平台層)、#179(PR 5 drift)、#180(PR 6 log 與設定)、#181(PR 7 錯誤模型)、#182(PR 8 網路層)、#183(PR 9a JS 執行環境)。#176 是 CI 路徑探測,已關閉。
- **已合併進 `main`**:#173(設計文件)、#174(PR 1 指令檔分家)、#175(PR 2 骨架與 CI)、#177(PR 3 lint)、#178(PR 4 平台層)、#179(PR 5 drift)、#180(PR 6 log 與設定)、#181(PR 7 錯誤模型)、#182(PR 8 網路層)、#183(PR 9a JS 執行環境)、#184(PR 9b 契約執行器)。#176 是 CI 路徑探測,已關閉。
- **isar/sqlite3 共存探針**:已完成,兩平台共存、全部 16KB 對齊(`research/isar-sqlite3-coexistence.md`,ADR 0010 已補)。
- **PR 9a 完成**(#183,子任務已 archive 到 `.trellis/tasks/archive/2026-09/09-30-js-runtime/`):每插件一個背景 isolate 的 QuickJS、宿主 API v1、manifest、從檔案安裝與 dev 開發入口、測試插件 `fmp-test`;數字在該子任務 `research/notes.md` §4。
- 實機:Windows dev 開發入口裝上、重啟後從資料庫載入、prod 不理會旗標;Android 模擬器 dev 的前兩項。模擬器上的 `com.personal.fmp` 是舊版 1.11.0,prod 沒裝上去驗;prod 那一段由單元測試守(`devPluginPath` 對 prod 一律回 `null`,有變異驗證)。
- **PR 9b 待合併**(分支 `feat/plugin-contract`,子任務已 archive 到 `.trellis/tasks/archive/2026-09/09-30-plugin-contract/`):fixture 錄製重播、`checks.json`、契約執行器;擁有者決定 8 讓執行器可在命令列做免登入的錄製(`live` tag),ADR 0015 §決定 7 已補更正。
- **之後**:9c(建 `1morr/fmp-plugins`、B 站插件、錄一次 fixture)→ YouTube.js 探針(與 10–13 並行)→ 10 播放核心 → 11 verify-on-device → 12 UI → 13 五平台建置與發版 workflow → 里程碑驗收。
- **PR 9b 完成**(#184,子任務已 archive 到 `.trellis/tasks/archive/2026-09/09-30-plugin-contract/`):fixture 錄製重播、`checks.json`、契約執行器;擁有者決定 8 讓執行器可在命令列做免登入的錄製(`live` tag),ADR 0015 §決定 7 已補更正。
- **PR 9c 完成**(子任務已 archive 到 `.trellis/tasks/archive/2026-09/09-30-bilibili-plugin/`):公開 repo `1morr/fmp-plugins`,本機 clone 在與 FMP 同層的 `fmp-plugins/`;B 站插件由該 repo 的 #1 合併(`e2b224b`),`bilibili/bilibili.js` 只有 `search`、`resolveStream`。
- 在 dev App 裝它:`fmp.exe --fmp-dev-plugin=<fmp-plugins>/bilibili/bilibili.js`(Android 照 9a 的 `run-as` 做法)。
- 真實連線:兩次錄製共 8 個 GET,沒有遇到風控;fixture 人工逐檔檢查過。
- **下一步**:FMP 遮蔽修正(「9c 留下的後續」前兩項,小 PR)→ YouTube.js 探針(與 10–13 並行)→ 10 播放核心 → 11 verify-on-device → 12 UI → 13 五平台建置與發版 workflow → 里程碑驗收。
- **擁有者決定**:1–8 都在父任務 `prd.md`「擁有者的決定」。9a、9b 期間新增了三項:
- 決定 6:插件安裝檔是單一 `.js`,開頭帶 `==FMP Plugin==` manifest;
- 決定 7:插件在背景 isolate 執行;逾時先送存活探測,沒回應才停用到重啟;
Expand Down Expand Up @@ -165,8 +168,8 @@
- [x] TypeScript 型別定義(9a)。
- [x] 契約執行器、fixture 格式、`checks.json`(9b,`app/test/plugins/contract/`)。
- [x] `test/fixtures/plugins/test_plugin/`:合成資料、本機音檔(9a)。
- [ ] 接真實 B 站時觀察:伺服器回不合法的 `Set-Cookie` 是否讓請求變成 `UnexpectedError`(`dio_cookie_manager` 的 `ignoreInvalidCookies` 預設 false;PR 8 檢查提出,沒有重現案例前不改)。
- [ ] 建立 `1morr/fmp-plugins`:
- [x] (9c 沒觀察到,移到「9c 留下的後續」)接真實 B 站時觀察:伺服器回不合法的 `Set-Cookie` 是否讓請求變成 `UnexpectedError`(`dio_cookie_manager` 的 `ignoreInvalidCookies` 預設 false;PR 8 檢查提出,沒有重現案例前不改)。
- [x] 建立 `1morr/fmp-plugins`(9c):
- `bilibili/` 的 `search`、`resolveStream`;
- 錄一次 fixture(真實連線,最少操作)。
- 實測(§7):
Expand All @@ -190,6 +193,14 @@
- [ ] 串流候選的網址本身(query 裡的 `access_key` 之類)不在憑證檢查內,現在只查 headers。9c 接 B 站時看要不要加。
- [ ] 9b 審查時有一次 `flutter test` 兩個契約測試檔卡在 loading(編譯階段,20 分鐘無進度),之後 14 次沒重現。CI 若出現同樣的卡住,從 flutter_tools 層查。

9c 留下的後續:

- [ ] 內建遮蔽名單缺 `hdnts`(Akamai)與 `buvid`(`app/lib/core/redaction/redaction_lists.dart` 的 `_bilibiliSigned`);官方插件靠 manifest 補上,使用者自寫的插件會漏。
- [ ] `Redactor` 只套用第一個符合的 `MediaCdn`(`redactor.dart` 的 `firstOrNull`),插件追加的 CDN 規則蓋不到內建已有的 host;應合併所有符合項的參數。
- [ ] 登入後 `_AuthInterceptor` 以 `headers.addAll` 注入 `Cookie`,會整個蓋掉插件送的匿名 `buvid3`;舊專案是合併。M3 登入任務決定合併或交給插件。
- [ ] B 站插件的 `rateLimit`(併發 2、間隔 300ms)沒有量測依據;`allowedHosts` 外的 PCDN(`szbdyd.com`、直接寫 IP 的節點)會被丟掉,舊專案不限制。M6 媒體 client 接上時一併看。
- [ ] PR 8 的 `ignoreInvalidCookies` 觀察:兩次錄製的回應都沒有 `Set-Cookie`,沒觀察到;留到會發 cookie 的端點(登入)。

### 探針:YouTube.js(擁有者決定 3)

- [ ] 9 合併後開一個子任務,時限約 2–3 個 session。
Expand Down
3 changes: 2 additions & 1 deletion .trellis/tasks/09-28-m1-skeleton-tracer/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"09-29-error-model",
"09-29-network-layer",
"09-30-js-runtime",
"09-30-plugin-contract"
"09-30-plugin-contract",
"09-30-bilibili-plugin"
],
"parent": "09-26-fmp-rewrite",
"relatedFiles": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{"file": "docs/adr/0014-script-source-plugins.md", "reason": "Plugin model, manifest, host API v1, install format, plugin repo"}
{"file": "docs/adr/0015-testing-gates-and-dev-environment.md", "reason": "checks.json, fixtures, contract runner, CLI recording correction"}
{"file": "docs/adr/0012-network-layer-and-accounts.md", "reason": "Media requests without credentials, Bilibili anonymous buvid"}
{"file": "docs/adr/0027-on-device-verification.md", "reason": "When real network is allowed and how to report it"}
{"file": ".trellis/spec/app/plugins/index.md", "reason": "How to write a plugin, check cases and fixtures"}
{"file": ".trellis/tasks/archive/2026-09/09-30-plugin-contract/research/notes.md", "reason": "Fixture and checks.json formats, recording rules"}
{"file": "lib/data/sources/bilibili_source.dart", "reason": "Legacy Bilibili behaviour that the plugin reimplements"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{"file": "docs/adr/0014-script-source-plugins.md", "reason": "Plugin model, manifest, host API v1, install format, plugin repo"}
{"file": "docs/adr/0015-testing-gates-and-dev-environment.md", "reason": "checks.json, fixtures, contract runner, CLI recording correction"}
{"file": "docs/adr/0012-network-layer-and-accounts.md", "reason": "Media requests without credentials, Bilibili anonymous buvid"}
{"file": "docs/adr/0027-on-device-verification.md", "reason": "When real network is allowed and how to report it"}
{"file": ".trellis/spec/app/plugins/index.md", "reason": "How to write a plugin, check cases and fixtures"}
{"file": ".trellis/tasks/archive/2026-09/09-30-plugin-contract/research/notes.md", "reason": "Fixture and checks.json formats, recording rules"}
{"file": "lib/data/sources/bilibili_source.dart", "reason": "Legacy Bilibili behaviour that the plugin reimplements"}
53 changes: 53 additions & 0 deletions .trellis/tasks/archive/2026-09/09-30-bilibili-plugin/prd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# fmp-plugins 與 B 站插件(M1 PR 9c)

父任務:`../09-28-m1-skeleton-tracer`(implement「9.」的 9c;擁有者決定 5、6、8)。前兩個 PR:
- 9a(#183):執行環境,封存在 `../archive/2026-09/09-30-js-runtime/`;
- 9b(#184):契約執行器,封存在 `../archive/2026-09/09-30-plugin-contract/`。

依據:
- ADR 0014 §決定 6:插件庫;
- ADR 0015 §決定 4–6 與兩則 2026-09-30 更正;
- ADR 0027 §決定 2:真實連線的條件;
- 父任務 design §4。

## 做什麼

1. **建立公開 repo `1morr/fmp-plugins`**(擁有者決定 5),本機 clone 在與 FMP 同層的 `fmp-plugins/`。
- 第一個 commit 直接進 `main`,內容:
- `README.md`:繁中,寫明開發中、與 FMP 的關係、跑契約測試的指令;
- `LICENSE`:MIT,與 FMP 相同;
- `.gitattributes`、`.gitignore`。
- B 站插件走該 repo 的 PR 合併,照 design §4。
- CI、`index.json`、插件頁都留在 M3。
2. **`bilibili/` 插件**,只有 `search` 與 `resolveStream` 兩個能力。
- 形式:單一安裝檔 `bilibili.js`,開頭帶 `==FMP Plugin==` manifest(決定 6)。
- 行為以舊專案 `lib/data/sources/bilibili_source.dart`(search、getAudioStream 及相關私有方法)為規格,用 JS 重寫(ADR 0008 檔頭補充)。重點:
- WBI 簽名,md5 來自宿主的 `crypto`;
- 匿名 `buvid` 存在插件自己的 `storage`;
- `Referer` 與 `User-Agent` 的要求;
- 優先取 DASH 音訊;
- 錯誤碼對到結構化錯誤。
- 串流的 headers 只帶播放需要的(Referer、User-Agent),不帶 Cookie(ADR 0012)。
- `allowedHosts` 只列實際用到的網域。
- 要登入才有的東西(高音質等)不在 M1。
3. **`checks.json`**:每能力一條案例。
- `search` 用常見關鍵字;
- `resolveStream` 用一支長期存在、公開、無地區限制的影片。
4. **錄製 fixture**(決定 8、ADR 0027 §決定 2):
- 用 9b 的錄製模式對 B 站真實連線錄一次,只做最少請求。
- 寫檔前經過遮蔽。
- 錄完用重播模式跑契約測試,要全綠。
- 逐檔人工確認沒有 cookie、token、buvid 或簽名的原值,也沒有個人資訊。
- 回報「模式:真實」與做了哪些請求。
5. **FMP 端**:只有文件與任務紀錄。
- M1 交接段寫明 fmp-plugins 的位置、B 站插件的 commit,以及怎麼在 dev App 裝它(開發入口)。
- 若 9b 的執行器接 B 站時需要修正,另開 FMP 的修正,不混在這裡。
6. **PR 8 的後續**:觀察伺服器回不合法的 `Set-Cookie` 時,請求會不會變成 `UnexpectedError`,記下結論。

## 驗收

- [ ] `1morr/fmp-plugins` 是公開 repo,`main` 上有 README 與 LICENSE。B 站插件經該 repo 的 PR 合併。
- [ ] 在 FMP 的 `app/` 內執行 `FMP_PLUGIN_DIR=<fmp-plugins>/bilibili flutter test test/plugins/contract/contract_test.dart`,重播全綠。
- [ ] fixture 經人工逐檔檢查,沒有憑證原值。
- [ ] Windows dev App 用開發入口裝上 B 站插件,身分頁列出它。真正的搜尋與播放在 PR 10、12 驗。
- [ ] 真實連線的請求次數與內容寫進回報。
Loading
Loading