需要登录后才能观看
本文面向规则作者,讲解「搜索或观看前必须登录的站点」怎么写规则:在规则里声明 needLogin: true 与 loginURL,由 App 在搜索前引导用户在内置 WebView 完成登录,并自动托管登录 Cookie。
适用场景(先给结论)
当一个站点在搜索、详情或播放前强制要求登录(未登录就跳登录页 / 返回空结果 / 直接 403),就在该规则里加上 needLogin: true 和 loginURL:App 会在搜索前先拦截,引导用户在内置 WebView 里登录一次,之后该插件的搜索、观看请求自动携带登录 Cookie。
这不只针对次元城(ciyuancheng)这一个站点——任何「不登录就用不了」的同类站点都这么配。写法完全通用,只是每个站的 loginURL 与搜索 / 选集字段要换成该站自己的。
官方 Kazumi 规则模型里没有 needLogin、loginURL 这两个字段。樱花动漫 fork 在插件模型(my/lib/plugins/plugins.dart)上扩展了这两个字段,用于标记「本规则必须登录后才能搜索 / 播放」。官方文档对「需要登录」的原话是【官方原文】:
在目标网站中使用任意作品名称执行搜索,确认网站能够直接返回结果。若搜索前必须登录、完成验证码或经过复杂的人机验证,需要额外配置 Cookie 或反爬虫验证,不属于本教程的基础流程。
也就是说,官方版规则 JSON 本身不描述登录流程;是本 fork 把「登录态」做成了内置能力。下面的字段与行为均为【fork 源码】口径。
字段说明
按插件模型的 JSON 解析与序列化实现,只需在规则根级加两个字段:
| JSON 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
needLogin | bool | false | 为 true 时表示本规则需要登录验证:搜索前必须先登录,否则搜索被拦截。 |
loginURL | string | "" | 登录页地址。解析时兼容旧键名 loginUrl(json['loginURL'] ?? json['loginUrl'] ?? ''),序列化时统一写回 loginURL。 |
新旧键兼容:历史规则里若写成小写驼峰 loginUrl,App 仍能读出来;但新建或导出规则时请统一使用 loginURL(大写 URL)。
行为说明
标记 needLogin: true 后,App 按下面的流程工作(【fork 源码】,搜索拦截逻辑见 plugin_search_service.dart):
- 搜索拦截、不发请求:执行搜索时,若
needLogin == true且该插件尚未保存有效 Cookie,搜索直接被拦截,不发起任何网络请求;插件状态被标记为「需要登录」。 - 内置 WebView 打开登录页:用户在插件页触发登录后,应用通过内置 WebView 打开
loginURL加载登录页。 - 登录跳转后按插件名存 Cookie:用户在 WebView 内完成登录、页面发生跳转后,应用抓取该 WebView 中的 Cookie,并以插件名(
plugin.name)为键保存;之后该插件的搜索、观看请求自动携带这些 Cookie。 - Cookie 过期后重新登录:Cookie 有效期由
cookieTtlDays控制,可在规则设置中调整;过期后视同「未登录」,再次触发第 1 步拦截,需重新走 WebView 登录一次即可。
MCP 服务的字段白名单(mcp_server.dart)已把 needLogin、loginURL 列为「本 App 扩展字段」:通过 MCP 生成规则时,这两个字段会被正常识别,不会被当成未知字段丢弃。
真实示例:次元城(ciyuancheng)
下面是仓库里正在使用的次元城插件完整规则,逐字照抄:API 模式(api: "8"),用 searchApiConfig / chapterApiConfig 配 JSON 接口,并开启 useWebview 走真实浏览器内核。重点看根级的 needLogin、loginURL 与 icon。
{
"api": "8",
"type": "anime",
"name": "ciyuancheng",
"version": "3.0",
"muliSources": false,
"useWebview": true,
"useNativePlayer": true,
"usePost": false,
"useLegacyParser": false,
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
"baseURL": "https://www.cycani.org/",
"referer": "https://www.cycani.org/",
"searchURL": "",
"searchList": "",
"searchName": "",
"searchResult": "",
"chapterRoads": "",
"chapterResult": "",
"searchMode": "api",
"chapterMode": "api",
"searchApiConfig": {
"request": {
"method": "GET",
"url": "https://www.cycani.org/api/videos/search",
"query": {
"q": "@keyword",
"page": 1,
"page_size": 24
},
"headers": {
"Accept": "application/json",
"X-App-Name": "cyc_web",
"X-App-Version": "cycweb",
"X-Time-Zone": "Asia/Shanghai"
}
},
"listPath": "$.data.list[*]",
"namePath": "$.title",
"sourcePath": "$.video_id"
},
"chapterApiConfig": {
"request": {
"method": "GET",
"url": "https://www.cycani.org/api/videos/@source/sections",
"query": {
"player_code": "cychub",
"page": 1,
"page_size": 48
},
"headers": {
"Accept": "application/json",
"X-App-Name": "cyc_web",
"X-App-Version": "cycweb",
"X-Time-Zone": "Asia/Shanghai"
}
},
"format": "nested",
"roadsPath": "$.data",
"roadNamePath": "",
"episodesPath": "$.list[*]",
"episodeNamePath": "$.title",
"episodeUrlPath": "",
"episodePage": {
"url": "/anime/@source/play/@episodeNumber?from=cychub",
"query": {}
}
},
"needLogin": true,
"loginURL": "https://www.cycani.org/login",
"icon": "https://www.cycani.org/favicon.ico"
}- 导入后先在 App 内登录再搜索:这份规则导入后第一次搜索会被拦到「需要登录」,在插件页点进去用 WebView 登录次元城账号,之后搜索、选集才正常。
loginURL指向https://www.cycani.org/login这个独立登录页;登录跳转后 App 自动抓取 Cookie 并按插件名ciyuancheng保存。useWebview: true:整站走真实 WebView 内核加载,和登录态、JS 渲染配合更稳。- API 搜索 / 选集各自带了
X-App-Name、X-App-Version等请求头——这类头是该站接口自己要求的,不是登录字段,照抄该站即可。
通用写法:XPath 站怎么配
次元城是 API 模式的真实例子。如果你写的是普通 XPath 规则,同样只要在根级加 needLogin + loginURL,其余 XPath 字段照常规写:
{
"api": "1",
"type": "anime",
"name": "示例需登录站点",
"version": "1.0",
"baseURL": "https://login-required.example.com/",
"needLogin": true,
"loginURL": "https://login-required.example.com/login",
"searchURL": "https://login-required.example.com/search?wd=@keyword",
"searchList": "//div[@class='search-item']",
"searchName": "//h3/a",
"searchResult": "//h3/a",
"chapterRoads": "//div[@class='playlist-box']",
"chapterResult": "//ul/li/a",
"userAgent": "",
"referer": "https://login-required.example.com/",
"searchMode": "xpath",
"chapterMode": "xpath",
"antiCrawlerConfig": {
"enabled": false,
"captchaType": 1,
"captchaImage": "",
"captchaInput": "",
"captchaButton": "",
"captchaDetectType": 1,
"captchaDetectValue": "",
"captchaScript": ""
}
}给任意一个「要登录才能用」的新站写规则时,走这三步即可(不限于次元城):
- 实测确认登录拦截:先用浏览器 / curl 不登录直接搜索,确认确实是「未登录就跳登录页 / 空结果 / 403」,而不是验证码或别的反爬。
- 加上
needLogin: true+loginURL:loginURL填该站真实登录页地址,搜索 / 选集字段仍按常规 XPath 或 API 方式写好。 - 提示用户在 App 内登录:在规则说明里提醒用户——导入后先在插件页用内置 WebView 登录一次,再回来搜索。
判定不清时先别急着加 needLogin:如果搜索被拦后出现的是图片验证码 / 滑块 / Cloudflare 风控而不是登录页,那是「人机验证」问题,请看 人机验证应对,不要和登录态混为一谈。
注意事项
- 不要在规则 JSON 里硬编码账号密码:登录动作由用户在 WebView 里人工完成,规则只声明「需要登录 + 登录页地址」。Cookie 由应用托管、按插件名保存在本机,不会随规则文件泄露。
loginURL可以填首页:若站点的登录入口是首页弹窗而不是独立的/login页,直接把loginURL填成首页地址也行——App 只是用 WebView 打开它,登录完成跳转后照样抓取 Cookie。- Cookie 有有效期,过期重登即可、不必改 JSON:过期后搜索会被重新拦回「需要登录」状态,重新打开登录页登录一次就行,不需要修改规则 JSON。TTL 天数可在规则设置里调。
- 可与
antiCrawlerConfig并存:一个站点完全可能既要登录、又有验证码(登录后搜索还弹验证码)。两类配置互不冲突,按需同时配置即可;反爬只作用于 XPath 搜索阶段被验证页替换的情形。 - Cookie 是个人会话凭据:按插件名保存在本机,不要把他人导出的 Cookie 或登录态带进自己公开的规则里。
相关文档
樱花动漫 · 基于 Flutter · 开源 GPL-3.0 · 规则格式兼容 Kazumi