Repository navigation
fix(drivers/lanzou): support new /fn download page structure - #147
Open
Wudarensheng wants to merge 2 commits into
Open
Wudarensheng wants to merge 2 commits into
Wudarensheng wants to merge 2 commits into
Conversation
Lanzou moved the share download params into the /fn?<token> inner page, and the download API is now an absolute URL (https://apifile.woozooo.com/ajaxfile.php?file=N) that requires sign params (wp_sign / ajaxdata, action=downprocess, websign=2, kd=1, ves=1). The old relative-path regex ('/ajaxm.php?file=N') no longer matches, so parsing silently fell back to the wrong host with an incomplete form. - add parseFnPage() to extract the apifile URL and sign form, and use it when the legacy regex does not match (legacy flow kept untouched) - keep the previous lenient extraction as a last-resort fallback - fix removeJSComment() treating the '//' in https:// as a line comment, which truncated absolute URLs on the share page - use an absolute iframe src as-is instead of prefixing the share domain Adds unit tests for the new parser and the driver-level flow, and fixes the stale mock in scripts/test-lanzou.mts (it matched ajaxm.php while the driver posts to ajaxfile.php, so that case never actually ran). Ports OpenListTeam/OpenList@e1d88b0 (fix(drivers/lanzou): support new /fn download page structure, #3157).
pikachuren
previously approved these changes
Oct 26, 2026
pikachuren
left a comment
Collaborator
There was a problem hiding this comment.
代码评审总结
这是一个针对蓝奏云 2026-10 改版的精准适配 PR,解决了新版 /fn 内页结构变化导致的下载链接解析失败问题。
✅ 优点
- 问题定位准确: 清楚识别了改版后的两个关键变化:
- 下载接口从相对路径改为 apifile 绝对地址
- 签名参数从 data 对象改为独立变量(wp_sign/ajaxdata)
- 向后兼容: 同时支持新旧两种内页结构,不破坏已有功能
- 测试覆盖充分: 新增专门测试用例验证改版结构,覆盖绝对地址解析、签名透传、kd参数等关键逻辑
- 注释清晰: help.ts 中对 https:// 不当作注释的处理有详细说明
- 测试用例设计细致: mock 返回结构贴近真实改版页面,断言覆盖 URL、签名字段、参数值
📝 代码质量
help.ts 变更:
- ✅ JS 注释解析修复合理:
!result.endsWith(":")避免将https://误判为注释 - ✅ 注释说明清晰,指出改版后大量使用绝对地址的背景
util.test.ts 新增测试:
- ✅ mock fetch 设计完善: 覆盖 /fn 内页、POST 下载接口、302 跳转、最终 CDN 链接
- ✅ 断言粒度细: 验证 URL 前缀、sign/signs/kd 参数值、最终 raw_url
- ✅ 测试用例命名清晰: "适配改版 /fn 内页(apifile 绝对地址)"
driver.test.ts 变更:
- ✅ 向后兼容: ajaxm.php 和 ajaxfile.php 双重判断,支持新旧接口
- ✅ 测试数据真实: mockIframe 结构符合实际改版页面(domain1/wp_sign/ajaxdata)
💡 改进建议
1. 增加旧版结构回归测试
当前新增测试只覆盖改版结构,建议确认旧版测试仍然通过:
test("Lanzou Driver get() 保持旧版 /fn 内页兼容性", async () => {
// 使用 LEGACY_FN_PAGE 结构(相对路径 /ajaxm.php + data 对象)
// 验证 $.ajax 解析和参数传递仍正常工作
})2. 错误处理增强
如果遇到既不是新版也不是旧版的意外结构,建议增加友好错误提示:
if (!domain1 && !ajaxUrl) {
throw new Error(
"蓝奏云页面结构无法识别,可能需要更新驱动适配新版本"
)
}3. 正则表达式健壮性
https:// 判断逻辑依赖 endsWith(":")。如果未来出现其他 URL scheme(如 http://),建议改为更通用的正则:
// 改进: 检查 // 前是否为 URL scheme
else if (nextChar === "/" && !/[a-z]+:$/i.test(result)) {
// 这不是 http:// 或 https://,按行注释处理
inSingleLineComment = true
}4. 测试覆盖边界场景
建议增加以下测试:
test("Lanzou get() 处理混合结构(新旧字段共存)", async () => {
// 如果页面同时包含旧版和新版字段,验证优先级
})
test("Lanzou get() 处理签名字段缺失", async () => {
// wp_sign 或 ajaxdata 为空时的降级行为
})🔍 潜在风险评估
- API 稳定性: 蓝奏云已进行一次改版,未来可能再次变更。建议在生产环境监控解析失败率
- 向后兼容测试: 需确认旧版结构的现有测试仍然通过,避免回归
- 特殊字符处理:
https://判断依赖字符串尾部检查,如果 URL 前有其他字符可能误判
🎯 总体评价
这是一个精准且务实的适配 PR。问题定位准确,解决方案简洁,测试覆盖到位。同时保持向后兼容,不破坏现有功能。注释清晰,测试用例设计贴近真实场景。
评分: 9/10
建议:
- ✅ 可以立即合并
- 🧪 建议补充旧版结构回归测试,确认向后兼容
- 📋 建议在文档中记录改版适配历史,便于未来维护
- 🔍 生产环境监控解析失败率,及时发现新的结构变化
优秀的快速适配工作! 👍
Review follow-ups for the /fn page structure fix: - document parseFnPage()'s return contract: null means "not a 2026-10 /fn page", not an error, and the caller must fall back to the legacy parse - report an actionable error when the download page matches neither the legacy nor the /fn structure (previously "not find file id", which reads like a parse bug rather than "Lanzou changed again") - tests: parseFnPage returns null when wp_sign/ajaxdata are missing, legacy markers take priority on a page carrying both structures, unrecognized page yields the new error, and a driver-level legacy regression case so backward compatibility is asserted explicitly No behavior change for legacy pages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(drivers/lanzou): 适配蓝奏云 2026-10 新版 /fn 下载页
Summary / 摘要
蓝奏云在 2026-10 改版了单文件分享页:下载参数被移进
/fn?<token>内页,下载接口从相对路径'/ajaxm.php?file=N'变成绝对地址https://apifile.woozooo.com/ajaxfile.php?file=N,并且必须携带签名参数(页面里的
wp_sign/ajaxdata→action=downprocess、websign=2、kd=1、ves=1)。旧正则在改版页面上不再命中。更麻烦的是它不会直接报错:宽松正则会从 apifile 绝对地址里「蒙对」
file ID,然后 POST 到错误的主机、带着缺字段的表单,最终表现成
fetch failed。结果是 url(公开分享)模式下所有单文件分享都取不到直链。
用户可感知的行为变化 / User-visible changes
doupload.php链路,本次未改动)。重要实现变化 / Implementation changes
parseFnPage():从/fn内页提取 apifile 绝对地址与签名表单;未命中改版结构时返回null。getFilesByShareUrl()的无密码分支改为三级解析:旧版严格正则 →parseFnPage()(原样 POST 到绝对地址)→ 原有宽松兜底。旧版流程保持不变。
postShareAjax(),统一「POST → JSON 解析 →zt校验」。removeJSComment()不再把https://里的//当作行注释。改版页面大量使用绝对地址,原实现会把
src="https://..."从//处截断,导致连 iframe 都取不到。src已是绝对地址时直接使用,不再拼shareBaseDomain(原实现会拼成https://hosthttps://host/fn?...)。src/backend/drivers/lanzou/util.test.ts(6 例);修正scripts/test-lanzou.mts中一直失效的 mock(它只匹配
ajaxm.php,而驱动一直 POST 的是ajaxfile.php,所以那条用例从未真正跑过),并补一条改版结构的驱动级用例。
兼容性 / Compatibility
无破坏性变更:旧版页面结构仍走原分支,行为不变。
无配置项、存储格式、迁移或公开 API 变化。
与上游 Go 实现
OpenListTeam/OpenList@e1d88b0(#3157)语义对齐。This PR has breaking changes.
/ 此 PR 包含破坏性变更。
This PR changes public API, config, storage format, or migration behavior.
/ 此 PR 修改了公开 API、配置、存储格式或迁移行为。
This PR requires corresponding changes in related repositories.
/ 此 PR 需要关联仓库同步修改。
Related repository PRs / 关联仓库 PR:
e1d88b0(#3157)合入,本 PR 是 Worker 侧移植。Related Issues / 关联 Issue
Relates to OpenListTeam/OpenList#3157
Testing / 测试
go test ./...—— 不适用:本仓库是 TypeScript(Hono + Workers),Go 实现在上游仓库。自动化
说明:
tsc全量仍有 8 处既有报错(src/backend/drivers/quark_uc_tv/driver.test.ts6 处、src/backend/internal/model/db_cipher.test.ts2 处),与本 PR 无关,未一并处理。npm run build的前端拉取步骤在本机失败(git clone触发 schannelCRYPT_E_NO_REVOCATION_CHECK,环境网络问题),因此只跑了build:edge(TS 打包步骤)验证。实机测试(生产环境,2026-10-22,本机直连)
pan.lanzoui.com/b158157→iYROC0ey8sjapifile…file=2425169;直链 Range 请求 HTTP 206,首字节PKpan.lanzoui.com/b174576→134136697修复前后对照(同一张真实
/fn页面):file=2425169pan.lanzoui.com/ajaxfile.php?file=2425169→ HTTP 407(空 body)apifile.woozooo.com/ajaxfile.php?file=2425169→ HTTP 200、zt=1websign,kd=0kd=1fetch failed,无直链Checklist / 检查清单
/ 我已阅读 CONTRIBUTING。
/ 我确认此贡献符合仓库许可证、贡献规范和行为准则。(待作者确认)
gofmt,go fmt, orprettierwhere applicable./ 我已按适用情况使用
gofmt、go fmt或prettier格式化变更代码。/ 我已在适用情况下请求相关维护者或代码所有者审查。(待作者操作)
关于格式化的说明:
util.ts/help.ts在基线上本就不符合仓库 prettier 配置——.prettierrc只设了
semi: false,printWidth走默认 80,而既有代码按约 100 列书写。对整文件执行prettier --write会产生数百行与本 PR 无关的重排,因此新增代码只沿用文件既有风格,未整体重排;新增的
util.test.ts通过prettier --check。若维护者希望顺带统一格式,建议单独开一个style:PR。CI 目前没有格式校验。AI Disclosure / AI 使用声明
/ 此 PR 包含 AI 辅助内容。
Tools used / 使用工具:
Usage scope / 使用范围:
Code generation / 代码生成
Refactoring / 重构
Documentation / 文档
Tests / 测试
Translation / 翻译
Review assistance / 审查辅助
I have reviewed and validated all AI-assisted content included in this PR.
/ 我已审核并验证此 PR 中的所有 AI 辅助内容。
I have ensured that all AI-assisted commits include
Co-Authored-Byattribution./ 我已确保所有 AI 辅助提交都包含
Co-Authored-By归属信息。I can reproduce all AI-assisted content included in this PR without any AI tools.
/ 我可以在没有任何 AI 工具的情况下重现此 PR 中包含的所有 AI 辅助内容。