477 Commits
Author SHA1 Message Date
huangxd- afc8b8119f chore: 更新 README 项目结构说明并调整 forward 测试用例参数以同步文档与测试数据
在 README 项目结构中补充 .pr_agent.toml、pr_agent.yml 和 icons.js 的说明;将 forward 测试的 sourceOrder 从 hongguo 改为 douban,并更新测试标题为“早春晴朗”,使文档记录与新增配置/资源一致,测试样例保持最新。
2026-10-03 07:12:39 +08:00
Wo254992andhuangxd- 7f44d1a67f fix(cache): 隔离缓存后端并优先恢复 Local Redis 查询数据 (#492)
* fix(cache): 隔离缓存后端并优先恢复 Local Redis 查询数据

* fix(cache): 透传剧集 ID 写入失败并补充清理失败提示

- addAnime 的 ID 溢出/映射错误保留在请求级 detailStore,逐层合并后写入搜索响应 errorMessage,不再只留服务端日志
- 清缓存保存失败时提示未保存的后端仍保留清理前数据、重启后会重新加载
- 补充搜索透传与清理失败提示的回归用例

* fix(cache): 收敛剧集 ID 写入失败的提示范围并隔离诊断日志

- 诊断用的 animes 序列化移出主 try 并自带容错:历史快照里残留
  links 为 null 的条目时,序列化不再把已成功的写入判成失败,也不再把
  内部报错透给调用方。
- errorMessage 只在"没有结果"时承载写入失败原因,恢复与 dandanplay
  既有约定一致(其余响应点均与 success:false 配对);链接解析与合并
  分支恒有合成条目,不承载该提示。
- /api/v2/match 与 /api/v2/search/episodes 在无结果时透出同一原因,
  补齐上一提交只覆盖搜索接口的缺口。
- 逐源隔离存储的错误合并抽为 mergeAddAnimeError,去掉重复实现。
- 测试:溢出用例拆成"无结果"与"部分成功"两个方向,新增 links 为 null
  的历史快照不再拖垮后续写入用例。

* fix(cache): 限制本地 Redis 命令响应等待时间

- 初始化读取使用 5 秒响应期限,普通 GET/SET/SETEX 使用 30 秒期限
- 超时关闭对应连接并冷却 30 秒,释放在途请求;失败不更新保存 hash
- 复用现有 TCP 故障测试,覆盖读取回退、并发等待、写入失败及冷却后重连
- 影响范围仅限 Local Redis 工具与测试,不新增配置或依赖
- 验证:159 项测试通过,原始故障脚本复测通过,语法检查及 Node/neutral 打包通过

* fix(cache): 收敛编号恢复并限制缓存清理等待

- 编号兜底统一在初始化结束时判断,健康计数器或 ID 不受损坏副本影响
- 剧集链接分配失败时回滚本次追加的 ID 和计数器,保留既有详情与映射
- 各后端并发清理,Redis 清理使用包含 Local Redis 建连时间的 5 秒预算
- 保留持久化失败状态与失败后端提示,普通业务沿用原超时设置
- 复用持久化和 TCP 故障用例补充回归,说明编号兜底及备份语义

* fix(cache): 修复 Upstash 收藏热切换与读取失败假成功

* fix(cache): 补齐剧集编号引用并按详情批次校正上界

* perf(cache): 复用 Local Redis 批量保存的序列化结果

* fix(cache): 阻止热切换前的收藏请求修改新库

* test(cache): 将缓存回归测试合并到主测试文件

* test(cache): 扩大 Redis 超时测试的预算压缩范围,5 个场景耗时从 ~27s 降至 ~8s

- 业务命令 30s、握手 5s、批量/清理 5s 与 Upstash 5s 一并钳制到 1200ms,并新增 AbortSignal.timeout 钳制
- clear-write 假服务器慢握手停顿 3000ms 降至 600ms,保持仍慢于压缩后的命令预算
- 断言下限 4500ms 调整为 1000ms,上界收紧至 4000ms
- 新增断言验证清理路径剩余预算扣除已耗连接时间(budgets 记录原始值)

---------

Co-authored-by: huangxd- <649924757@qq.com>
2026-10-02 23:11:33 +08:00
马上回来 3aba4ad42a fix(dandan): 优化外文检索词处理,避免误杀正确命中并调整 TMDB 结果预过滤逻辑 (#493)
* fix(dandan): 优化外文检索词处理,避免误杀正确命中并调整 TMDB 结果预过滤逻辑

* fix(dandan): 优化外文检索词处理逻辑,增加标题归一化以提高匹配准确性
2026-10-02 21:39:13 +08:00
b4901e45e0 fix(mango): 正确排序综艺标题末尾的上中下分部 (#494)
* fix(mango): 正确排序综艺标题末尾的上中下分部

Prepared, adapted, tested and submitted by GPT-6 agent with the account owner's authorization. These changes were not manually authored or submitted by the account owner.

* refactor(mango): 简化分部判断并合并排序测试

Prepared, adapted, tested and submitted by GPT-6 for Samsung agent with the account owner authorization.

---------

Co-authored-by: GPT-6 Agent <agent@localhost>
Co-authored-by: huangxd- <649924757@qq.com>
2026-10-02 21:38:35 +08:00
Elegy233 f994b6d8f5 refactor(ui): GitHub 标识与请求参数标题改用图标库图形 (#490)
* refactor(ui): GitHub 标识与请求参数标题改用图标库图形

GitHub 标识不再从 wikimedia 拉取 Octicons 图标,改为内联 GitHub 官方 Invertocat
图形并收入图标库;sprite 按线性图标与品牌标识分别生成 symbol,品牌标识沿用官方
0 0 98 96 viewBox 与原始路径(填充取 currentColor),不套用线性图标的 24×24 描边
规则。

「请求参数」标题原由 📋 字符标记,改为花括号图形:该标题下方即请求参数的 JSON 键值
对文本,花括号与所标识的内容一致。

底部链接栏的三处 TG emoji 保持不变,仅将链接内图标与文字改为 flex 居中、间距由
gap 设定;footer-bar 整体 opacity .75 与链接文字的 --theme-muted 保持不变,图标
取 --theme-text,保持图形在链接文字之上的对比层级。

* fix(ui): 顶部 Logo 图片圆角改由容器裁剪,消除边缘露出的主题色

.logo 的圆角桌面为 12px、窄屏为 10px,而 .logo img 的圆角固定为 12px 未跟随
响应式,两层不同半径的圆角叠加后,图片圆角外会露出一圈容器背景,Logo 完全加载
后依旧可见(36px 的 Logo 上角部错位近 0.6px);桌面尺寸下两层半径相同,仅剩两层
圆角各自抗锯齿带来的边缘差异。

图片不再单独设置圆角,改由 .logo 已有的 overflow: hidden 统一裁剪,圆角只绘制
一次并自动跟随容器的响应式半径。

* fix(ui): 页头 Logo 图片补上空 alt 属性

页头 Logo 的 img 缺少 alt,屏幕阅读器取不到可读文本时会退回朗读图床地址,图片
加载失败时浏览器也会显示破碎图标与地址文字。Logo 旁已有站名标题,该图片属装饰
性质,以空 alt 标记交由辅助技术跳过,写法与页面内其它装饰性封面图一致。

* 更新背景弹幕词库

Updated the Danmaku dictionary with new phrases and removed some old ones.

* Update main.js
2026-10-02 21:36:04 +08:00
Elegy233 d91079ebc1 feat(dandan): 弹弹302关联链接仅分发给已在 SOURCE_ORDER 开启的源 (#489)
关联链接的对应平台只要已接入采集源就会实时拉取,与 SOURCE_ORDER 表达的可选源范围无关。现要求该源已在 SOURCE_ORDER 中开启才分发,未开启的源与已独立选择的合并源一样跳过并记录日志。README、config/.env.example 与 envs.js 中 DANDANPLAY_ACCOUNT 的说明同步为「并把同一请求下发的弹弹关联链接分发给对应平台源实时拉取(需开启对应源)」。

worker.test.js 新增用例:仅开启 bilibili 时只拉取 bilibili、仅开启 tencent 时分发目标随之切换、两个关联源均未开启时不拉取、已开启但被独立选择的合并源覆盖时同样跳过。
2026-10-02 21:35:44 +08:00
e8851c5bc5 fix(forward): 构建独立 widget 时隔离服务端本地弹幕存储 (#495)
* fix(forward): 构建独立 widget 时隔离服务端本地弹幕存储

Prepared, adapted, tested and submitted by GPT-6 agent with the account owner's authorization. These changes were not manually authored or submitted by the account owner.

* test(forward): 将构建回归测试合并到主测试文件

* chore: 更新版本号至 1.21.4

---------

Co-authored-by: GPT-6 Agent <agent@localhost>
Co-authored-by: huangxd- <649924757@qq.com>
2026-10-02 21:35:14 +08:00
huangxd- fc1b7ff6ad fix(sources): 为 custom 源适配器传递 isolatedDetailStore (#488)
为 custom 源的 handleAdapter 增加 isolatedDetailStore 参数,并透传给 handleAnimes,确保自定义源能够使用与其他源一致的隔离详情存储,避免因缺少该参数导致处理逻辑不一致。
2026-09-26 16:21:51 +08:00
Elegy233andhuangxd- c42877ccf0 feat(dandan): 弹弹play弹幕替代机制改用 NipaPlay 中转弹弹play服务端获取弹幕与弹弹302关联链接(需登录) (#487)
* feat: 弹弹play弹幕替代机制改用 NipaPlay 中转弹弹play服务端获取弹幕与弹弹302关联链接

dandan 源原经弹幕代理获取弹弹缓存弹幕,仅在弹幕为空时借弹弹302关联链接拉取跨平台弹幕。现改为:配置弹弹play账号后,dandan 源经 NipaPlay 中转弹弹play服务端获取弹幕,并把同一请求下发的弹弹302关联链接分发给已接入的对应平台源实时拉取,两部分沿用既有多源去重合并。

- 源配置新增 DANDANPLAY_ACCOUNT 与 DANDANPLAY_PASSWORD,账号与密码同时填写后自动开启,移除 NIPAPLAY_REPLACE_DANDAN 开关
- 密码配置页提供连通性测试,新增 POST /api/nipaplay/verify
- NipaPlay 中转弹弹play服务端以香港线路为主、国内线路为备,主线路连续失败达到阈值后临时切换备用线路
- 原生弹幕地址请求失败时保留已解析的弹弹302关联链接,仍交由自采源拉取
- 以内嵌应用密钥与签名换取关联链接的做法改为账号令牌登录,令牌临近过期自动续期
- 凭据类变量与文本类变量同样绕过 dotenv 注释截断,含 # 时无需转义即可完整读取,写入 .env 后即时生效
- AI API Key 与 B站 Cookie 各自独立的编辑器样式合并为通用凭证编辑器样式,供 AI API Key、弹弹play密码与 B站 Cookie 共用
- nipaplay-util.js 日志标签按源与服务模块标注为 [dandan] [nipaplay]
- README 与 .env.example 新增两个账号变量的配置项与说明,配置预览新增「NipaPlay 中转弹弹play服务端」分组

* fix: httpRequestMethod 统一支持 allow_redirects 并修正失真 JSDoc

httpGet/httpPost 已实现 allow_redirects,httpRequestMethod(PATCH/PUT/DELETE 委托)此前未实现,其 JSDoc 标注「暂未实现」失真。现于 httpRequestMethod 统一实现:默认跟随重定向,false 时禁止以截获 3xx Location,与 GET/POST 行为一致;同步修正 JSDoc,并移除与 JSDoc 语义重复的那行注释。

* fix: 管理接口凭据写入日志脱敏,以按 encrypt 登记的变量为判据

- setEnv 日志对凭据类变量以 *** 脱敏,避免令牌/密码/Cookie/带令牌 URL 明文进入运行日志;判据为 Envs.sensitiveKeys(由 Envs.get 在 encrypt 为真时登记),非凭据变量保留明文以便排障
- 保存接口对掩码值 *** 跳过写入,避免凭据编辑框未改动直接保存时用星号覆盖服务端已存的真实值
- 补全 Envs.get 缺失的 encrypt 参数说明

* fix: applyShiftToDanmu 复用通用偏移工具处理关联链接偏移

原手写实现直接对 p 首字段与 t 做加减,缺少通用偏移工具的非负钳制,负偏移且绝对值大于弹幕时间时会产出负时间戳。
现改为调用 offset-util 的 applyOffset,与手动 @偏移、DANMU_OFFSET 等其余偏移路径行为一致,并保留 isRealTimePulled 标记供 dandan 源 formatComments 跳过专属转换。
worker.test.js 补充负偏移钳制用例。

* test: 补充重定向一致性与 dandan 实时拉取标记的回归用例

worker.test.js 补充两处行为的回归用例:httpRequestMethod(经 httpPatch)的 allow_redirects 是否与 GET/POST 一致,以及 dandan 源 formatComments 是否按 isRealTimePulled 区分原生弹幕与实时拉取弹幕。两者均为可稳定回归的纯逻辑,纳入项目测试以免后续回归。

* refactor: dandan.js 模块级定义下移以与其它源的结构保持一致

将 DandanUserAgent、SOURCE_TO_PLATFORM、getRelatedDanmuViaNipaplay、fetchDandanComments 下移到导出类之后,使「获取弹弹play弹幕」分节注释与其它源一致地紧随导入与源实例之后。
上述标识符均在函数体或方法体内使用,不在模块加载期求值,故下移不影响运行;已验证导出类方法仍可正常调用下移后的函数。

* fix: 弹弹play 与 AI 连通性检测改为随请求提交配置值

B站 Cookie 检测以请求体中的值向 B 站实时校验,弹弹play 与 AI 的检测则只能取到服务端运行期配置:弹弹play 的账号、AI 的地址与模型都不随检测请求提交。云部署(vercel 等)保存变量需重新部署才会刷新运行期配置,因此这两处检测在重新部署前一直失败。

现改为账号、密码、密钥、地址、模型一律随检测请求提交,取值自设置页已加载的配置;掩码值(当前访问无权限读取明文)视为未知,交由服务端回退到已保存配置。

worker.test.js 补充设置页请求体字段与弹弹play/AI 验证接口以请求体为准的用例。

* chore: bump version to 1.21.3

---------

Co-authored-by: huangxd- <649924757@qq.com>
2026-09-26 16:21:34 +08:00
Elegy233 280b2327cc fix: 标题比对统一繁简归一,修复开启 Bangumi Data 后无职转生第三季匹配错误 (#484)
* fix: 标题比对统一繁简,兜底匹配优先取目标季候选

季集匹配的标题判定依赖 normalizeSpaces 清理空格与修饰性符号,但不做繁简转换。当候选
标题为繁体、检索词为简体时(如本地数据中 無職轉生~到了異世界就拿出真本事~第三季),
「检索词是候选标题前缀」的条件无法成立,季门与跨季映射的候选筛选会把这类条目整体
排除,使正常匹配与跨季映射都无法命中该季,最终退回兜底取值而匹配到其它季。

- 新增 common-util.js 的 normalizeTitleForMatch(繁转简后再清理空格与符号),并在
  matchSeason、matchAniAndEp 的标题门与 findCrossSeasonEpisodeMap 的候选筛选中改用,
  使同一标题的不同繁简书写可参与季集判定
- 跨季映射与兜底共用的候选季号提取抽为 resolveCandidateSeason

fallbackMatchAniAndEp 在季集匹配与跨季映射均落空时按列表顺序取首个含目标集号的候选,
与候选所属季无关;改为先按候选季号分组,优先在季号与目标季一致的候选中取值,全部
落空时再退回原有取值顺序。该函数一并导出以便直接测试。

新增两个测试:季集匹配的繁简归一(覆盖季号不一致、季号插在主体名称中间、不同作品
不得误命中),以及兜底匹配的同季优先与回退行为。

* fix: 季号解析识别罗马数字季号

新增罗马数字季号识别,支持 Unicode 罗马数字字符(Ⅰ-Ⅻ),"无职转生Ⅲ"、
"OVERLORD Ⅳ" 等标题可解析出对应季号;ASCII 形式的 I/II/IV 与型号、英文缩写无法
区分,不参与识别。

* refactor: 标题比对统一繁简归一,整理归一化工具命名

标题比对统一使用 normalizeTitleForMatch(繁转简后剔除非文字字符),调用点不再各自
处理繁简差异:

- titleMatches 移除 query 侧的原词/简体/繁体变种池,两个入参统一归一化
- strictTitleMatch 的两个入参统一归一化
- bahamut 移除严格匹配与包含匹配中的双向繁简补丁及配套 try/catch
- bangumi-data-util 的 _flatText 改用该函数(原为 normalizeSpaces 套 simplized);
  检索词列表不再展开繁简变体,精确匹配排序改用归一化后比较
- offset-util 的剧名比对改用该函数,繁体规则名与简体剧名可互相匹配

归一化工具命名整理:

- normalizeSpaces 改名为 stripNonTitleChars:该函数按白名单剔除标题中的非文字字符
  (空格、标点、符号),原名的 Spaces 只反映其中一部分
- dandan-api 的 normalizeMatchTitle 改名为 prepareQueryTitle:它与
  normalizeTitleForMatch 词序相反、职责不同——前者准备查询标题并受
  ANIME_TITLE_SIMPLIFIED 开关控制,后者用于比对且无条件繁转简
2026-09-20 22:33:19 +08:00
Elegy233 f56b34f2a9 fix(animeko): 迁移镜像域名 (#483) 2026-09-20 22:32:54 +08:00
karasu 68cb8a9067 fix(local-danmu): 修复 ASS 控制标签残留、样式丢失和正文误删 (#482)
解析 ASS/SSA 样式、颜色、对齐及文本转义,保留尖括号正文并跳过绘图内容。修正样式重置语义,避免异常控制块和 Format 声明导致解析开销放大。

修正接口 JSON 展示的 HTML 转义,并补充正文、样式、时间轴和异常输入回归测试。

验证:node --test ./danmu_api/worker.test.js,131 项通过;真实 ASS 文件 1034 条正文、模式和颜色逐条核对通过。
2026-09-20 22:31:59 +08:00
lilixu3 a3044c06cf perf(local-danmu): 列表改用元数据索引,避免每次整份解析 (#480)
* perf(local-danmu): 列表改用元数据索引,避免每次整份解析

本地弹幕一多,搜索、匹配、弹幕请求都会变慢:listLocalDanmu() 会把
.cache/local-danmu 下每个文件的完整内容(含 comments,单集约 1.6MB)逐个
JSON.parse,120 集/122MB 时单次约 2.5 秒、峰值内存 300MB+,而且它出现在每次
search / comment 请求的路径上,会阻塞事件循环甚至把进程 OOM。

- 新增 .cache/local-danmu/index.meta(只存元数据):上传/删除/编辑时增量维护,
  缺失或损坏时按目录自动重建;读取按 mtime+size 做进程内缓存;索引读改写串行化
  避免并发上传互相覆盖;索引写失败时回滚数据文件
- 编辑(PATCH)按 resourceKey 取回完整资源再写回,避免整组编辑/回滚丢弹幕
- Redis 部署的 localDanmu:index 同样只存元数据,不再把整集弹幕复制进索引
- /api/v2/comment 分段请求跳过本来就不使用的本地兜底扫描

实测(120 集 / 每集 1 万条 / 122MB,Node 24):
listLocalDanmu 2.5s/次 → 首次 2.4s(建索引一次)+ 之后 3ms;
findLocalDanmu 2.5s → 3ms;本地源 search 2.6s → 6ms;峰值 RSS 331MB → 114MB。
5000 集规模:list 首次 60ms、find 9ms、search 44ms。

* fix(local-danmu): 修复索引并发重建覆盖与失败回滚丢数据

- 列表重建与上传/删除共用同一把锁,并在锁内复查,避免并发上传的条目被旧目录快照覆盖
- 覆盖已有资源时索引写失败恢复旧数据,新建失败才删除数据文件
- 删除改为先更新索引再删数据,索引失败时资源仍完整
- 列表发现孤儿/幻影条目自动重建;索引不可写时退化为本次扫描结果
- 编辑遇到索引有但数据缺失的条目时返回 404,不再写回纯元数据
- 测试复用 store 导出的文件名函数,避免重复实现 sha256
2026-09-20 22:31:13 +08:00
huangxd- 6fa090b556 Update globals.js 2026-09-20 22:30:48 +08:00
Elegy233 ea88a15a7a refactor(ui): 配置界面优化与图标补全,修正 TITLE_NOISE_FILTER 默认值 (#478)
* feat(ui): 配置页统一最近数据入口与操作按钮,修正合并模式已选源状态

- 新增 chevron-left 图标,返回按钮以图标替换手写左箭头符号
- 「查看最近数据」按钮与面板抽为 renderRecentDataButton / renderRecentDataPanel 供各配置页共用
- 标题过滤类配置(ANIME_TITLE_FILTER、EPISODE_TITLE_FILTER、TITLE_NOISE_FILTER)增加「查看最近数据」入口
- 「查看最近数据」统一右对齐,尺寸与同行操作按钮保持一致
- 修正 PLATFORM_ORDER 开启合并模式后已选源不可再选的问题,与 MERGE_SOURCE_PAIRS 行为一致

* fix: 修正 TITLE_NOISE_FILTER 默认值文档并补齐全角方括号

- README 与 config/.env.example 中 TITLE_NOISE_FILTER 默认值多写了一个反斜杠,字符类被
  提前闭合导致 new RegExp 抛 Unmatched ')',用户照抄该默认值会得到非法正则
- 默认规则补齐全角方括号 [],描述按段落换行,并改用 String.raw 使源码文本与正则内容一致
- MERGE_SOURCE_PAIRS 示例在描述、README 与 JSDoc 中统一为 dandan&bahamut&animeko,renren&hanjutv,renren
- worker.test.js 增加默认规则用例:默认值可直接编译并清理剧名杂音(含全角方括号),且
  README 与 config/.env.example 中的默认值必须与代码默认值一致

* feat(ui): 配置页操作按钮增加间距与图标

映射表、偏移规则、合并规则页的左右操作按钮在窄屏下无间距而贴边,
为按钮行增加间距;并为添加与解析类操作按钮增加线性图标,与查看最近
数据按钮的图标风格对齐。

- 左右操作按钮行增加间距
- 新增加号图标,用于添加映射项、添加规则按钮
- 解析并更新列表按钮使用刷新图标

* fix(ui): 修复添加规则切换按钮图标丢失

添加规则与收起规则切换按钮按状态写入图标:收起态使用向上箭头图标,
添加态使用加号图标。

* refactor(ui): 请求记录时间戳时钟 emoji 替换为线性图标

时间戳前的时钟 emoji 由 CSS 伪元素注入,改为在标记中渲染时钟
线性图标。

* refactor(ui): 日志查看与请求记录页头部间距与对齐调整

日志查看与请求记录页标题与操作按钮行之间增加间距;请求记录页
今日请求总数改用 flex 与刷新按钮居中对齐。

* fix(ui): 修复请求记录页在旧内核浏览器下的卡顿

最新版本徽标的呼吸动画叠加 backdrop-filter 模糊,缩放时持续重新
采样整页内容,旧内核浏览器软件合成下触发持续重绘,记录较多时卡顿。
去掉该徽标的 backdrop-filter,并为页头持续动画补充合成提示。

* fix(ui): 统一最新版本徽标暗色模式背景并清理无意义毛玻璃

最新版本徽标在暗色模式下沿用实底主题色覆盖,与浅色模式的半透明
背景不一致,删除该覆盖;并移除当前版本徽标上无意义的 backdrop-filter。
2026-09-13 13:10:36 +08:00
lilixu3 60bed960c5 fix(local-danmu): 修正 B 站 XML 弹幕颜色解析 (#476)
标准 B 站 XML 的 p 为 8 段(时间,类型,字号,颜色,...),颜色应取第 4 段;旧 3/4 段格式仍取第 3 段。补充回归测试覆盖两种格式。
2026-09-13 13:10:21 +08:00
lilixu3 cbb8f7bda9 fix(youku): cna 获取失败时本地生成兜底,避免 500 (#475)
- mmstat(log.mmstat.com) 被广告过滤/私人 DNS 拦截时,改用本地生成的 24 位 cna
  继续取弹幕;实测优酷弹幕接口只把 cna 当客户端标识回传,不校验其来源
- 首次失败后进程内复用兜底 cna,后续请求不再访问 mmstat
- getEpisodeDanmuSegments 所有失败分支统一返回空 SegmentListResponse,
  getEpisodeDanmu 增加空值兜底,避免 segmentList is not iterable 导致 HTTP 500
- 视频信息解析/时长校验、mtop token 缺失、弹幕属性解析增加防护
- 新增 5 个回归测试覆盖上述场景
2026-09-13 13:10:01 +08:00
huangxd- 0f02cdc036 feat(local-danmu): 优化本地弹幕 (#479)
* fix(ui): 让原生 select 下拉菜单跟随明暗主题配色

- 移除 html 上强制的 `color-scheme: light !important`,改为根据 body 的 data-color-scheme 对 select 单独设置明暗 color-scheme
- 为 select 的 option/optgroup 弹出菜单指定主题容器背景色与文字色,修复暗色模式下下拉弹窗仍为白底的问题
- 调整导航按钮顺序,将“本地弹幕”移至“请求记录”之前

* feat(local-danmu): 新增标题搜索过滤并默认折叠分组卡片

- 在本地弹幕列表上方新增搜索输入框,支持按标题关键词即时过滤已上传资源
- 缓存完整分组数据(localDanmuGroups),搜索时无需重新请求接口,并保留原有展开状态
- 渲染函数支持自定义空列表提示,搜索无结果显示“未找到匹配标题”,其余场景仍显示“暂无资源”
- 分组卡片默认改为折叠状态,避免资源较多时列表过长影响浏览
- 更新 worker 测试以适配渲染函数新的空文案参数及搜索相关交互

* feat(ui): 本地弹幕管理支持标题搜索、重新上传与整组删除

- 管理列表新增按标题关键词搜索,便于快速定位已上传的剧集
- 每集新增“重新上传”按钮,可直接替换原有弹幕文件
- 剧集分组新增“删除整个剧集”按钮,支持一次性删除整组资源
- 新增 local-danmu-group-remove、local-danmu-episode-actions 等样式类
- 同步更新根目录与 UI 模块 README 中本地弹幕功能的使用说明

* feat(local-danmu): 新增 PATCH 接口支持编辑本地弹幕元数据

- 新增 PATCH /api/v2/local-danmu/:resourceKey 接口:scope=resource 可修改集数与显示文件名,scope=group 可修改当前季的标题、年份、类型与季数
- 抽取 buildLocalDanmuMatchKeys 函数,复用上传与编辑时的匹配键构建逻辑
- 新增 validateEditFields 校验编辑字段的标题、年份与类型合法性
- 编辑仅更新元数据,不重新解析弹幕内容;目标资源已存在时返回冲突错误,避免覆盖原文件
- 同步更新 README 中本地弹幕接口及管理列表编辑功能的说明

* feat(ui): 支持电视剧本地弹幕文件批量导入

管理页面新增电视剧弹幕批量导入功能,提升多集上传效率:

- 同一部电视剧可多选文件一次导入,共用标题、年份和季
- 按文件名自动识别集数(如 S01E02、EP02、第02集、02.xml),
  支持逐个修改;未识别或重复的集数需先修正
- 页面逐个调用现有单文件上传接口,单个文件失败后继续处理
  其余文件,并显示各文件结果及成功、失败统计
- 电影仍逐个导入;同步更新 README 文档与批量预览样式
2026-09-13 13:09:37 +08:00
Elegy233 96602d6a7e refactor(ui): 前端图标统一为自绘线性 SVG 图标,替换原有 emoji 图标 (#473)
* refactor(ui): 界面图标统一为自绘线性 SVG 图标,替换原有 emoji 图标

界面中充当图标的 emoji 除底部彩色的 TG 链接外,全部替换为统一的线性图标。新增
ui/js/icons.js 图标库(32 个图标,24×24 网格、2px 圆头描边),以 SVG sprite 内联于
页面并通过 <use> 引用;图标尺寸随所在文字字号缩放,描边取 currentColor,颜色随容器、
八种强调色主题与明暗模式适配。

- 按钮、配置分类导航、搜索分组标题、状态行、徽标与映射行的图标全部替换
- 图标与文字并列处改用 flex 容器居中对齐
- 「重新部署」使用云与上行箭头,「弹幕配置」使用评论气泡
- 合并模式徽标切换改为只更新标签文本
- 为 .confirm-merge-btn:disabled 设置文字颜色

* refactor(ui): 配置分类导航统一渲染并补齐配置界面细节

- 映射表编辑界面(TITLE_MAPPING_TABLE、AUTO_MATCH_MAPPING_TABLE)增加「查看最近数据」按钮,仅查看缓存、不参与自动填充
- 配置预览与环境变量配置的分类按钮、搜索分组标题统一为同一渲染函数,消除两份平行实现
- 页头「当前版本」「最新版本」「API 端点」徽标补齐图标并对齐风格,当前版本为主视觉、最新版本为次要
- 各样式表头注释中「ACG 设计系统」描述改为「柔和圆角设计风格」

* feat(ui): 映射表查看数据入口调整与移动端头部、徽标动效优化

- 映射表编辑界面「查看最近数据」按钮移到「添加映射项」右侧并右对齐
- 移动端主标题「LogVar弹幕API」放大,突出主标题层级
- 「最新版本」徽标改为持续呼吸缩放动效,引导点击而不盖过「当前版本」

* feat(ui): 移动端标题微调、系统配置按钮缩小并补充弹幕词库贡献者

- 移动端主标题字号调整为 21px
- 移动端系统配置的导出/导入/清理缓存/重新部署按钮缩小
- 弹幕词库新增贡献者 @xlmc、@mz289、@sugarbliss
2026-09-12 07:51:27 +08:00
c81f63993c fix: fongmi 接口自动匹配增加季数比对,修复跨季同号集错配 (#471)
* fix: fongmi 接口自动匹配增加季数比对,修复跨季同号集错配

episode 文本中的季号此前被丢弃(S02E05 只取 E 后集数),第一季/第二季同号集得分完全相同,自动首条由源返回顺序决定:线上实测 庆余年 S01E01 与 S02E01 返回完全一致、S01E05 与 S02E05 亦逐字节相同。现从分集文本提取显式季号,与候选剧名的季标注比对:同季 +5000、跨季 -12000;任一侧无季标注不调整,电影/综艺日期场景保持原行为。

* Update worker.test.js

---------

Co-authored-by: xiangyang <xiangyang2001@outlook.com>
Co-authored-by: huangxd- <649924757@qq.com>
2026-09-12 07:50:30 +08:00
sugarbliss be4135ea04 fix: update fork sync actions for Node.js 24 (#470)
## Summary

Update the fork sync workflow actions to versions that support Node.js 24.

## Changes

- Update `actions/checkout` from `v4` to `v5`
- Update `aormsby/Fork-Sync-With-Upstream-action` from `v3.4` to `v3.4.3`

## Reason

GitHub Actions now reports the following deprecation warning:

> Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24.

`actions/checkout@v5` uses the Node.js 24 runtime, and
`Fork-Sync-With-Upstream-action@v3.4.2+` has also been updated for Node.js 24.
2026-09-12 07:44:33 +08:00
huangxd- b30be40b07 feat(ui): 云端部署未配置 Redis 时禁用本地弹幕功能
云端(非 Node)部署的本地弹幕依赖 Redis 存储,此前未配置 Redis 时上传/删除操作会静默失败。现向页面注入 redisValid 与部署平台状态,前端检测到云端环境且 Redis 不可用时,阻止上传、删除等写操作,并明确提示需配置 UPSTASH_REDIS_REST_URL 和 UPSTASH_REDIS_REST_TOKEN 后重试;权限说明区分该场景,配置加载后同步最新状态,worker 测试已同步更新。
2026-09-12 07:41:09 +08:00
huangxd- 1524494c45 feat(local-danmu): 新增本地弹幕上传、解析与管理功能 (#474)
- 新增 POST /api/v2/local-danmu/upload 接口,支持上传并解析本地弹幕文件(multipart/form-data,单文件最大 10 MB),电视剧可指定 season/episode
- 新增 GET /api/v2/local-danmu/list 获取按标题、年份、类型、季分组的资源列表;GET/DELETE /api/v2/local-danmu/:resourceKey 支持查询元数据与删除资源
- 接口需 TOKEN 或 ADMIN_TOKEN 鉴权,新增 LOCAL_DANMU_NOT_REQUIRE_ADMIN 环境变量控制上传/删除权限,默认仅管理员可操作,设为 true 时普通 TOKEN 用户也可执行
- Node/Docker 部署将资源保存至 .cache/local-danmu,云端部署使用 Redis 持久化
- SOURCE_ORDER 新增 local 源字段,可将本地弹幕搜索结果纳入排序(如 local,douban,360)
- 管理面板新增本地弹幕上传、列表管理与删除功能,并同步更新 README 接口与配置说明
2026-09-12 07:26:07 +08:00
huangxd- 28673ac764 fix: 修复sohu和maiduidui源 (#468)
* fix(maiduidui): 适配新版搜索接口及返回数据结构

- 更新搜索接口地址
- 根据 vodType 解析视频类型并过滤短视频
- 兼容缺失 yearName 的情况

* fix(sohu): 适配新版搜索签名并修复弹幕解析

* fix(danmu_api): 更新埋堆堆类型映射逻辑并修正注释

- 修正评论对`VOD_TYPE_MAP`的说明,区分`materialName`为题材标签而非类型
- 将类型过滤改为基于映射表直接判断,并移除对`materialName`的类型回退逻辑
- 统一类型名称来源,直接使用映射表中的值避免歧义
2026-08-30 11:15:57 +08:00
huangxd- 04a8a0cffa refactor: 引入源注册表,新增源只需加文件+注册一行 (#467)
* refactor: 引入源注册表,新增源只需加文件+注册一行

将散落在 dandan-api.js / worker.test.js 的 import + new + if/else 分发三段重复代码集中到 sources/registry.js 统一管理。

问题:每新增一个弹幕源需改 5 处(import、new、search 分发、handleAnimes 分发、getComments 分发),维护 3 段重复的 if/else 链(各 22 个分支),违反开闭原则。

方案:新建 sources/registry.js 注册表,集中管理 23 个源的元数据与实例化:
- 拓扑排序按依赖顺序实例化(douban 依赖 5 源、tmdb 依赖 douban 自动注入)
- handleAdapter 适配器统一各源 handleAnimes 签名差异(vod 遍历多服务器、custom 少参)
- extraSearchArgs 标记处理 vod 源 search 多参数特例
- logName 字段统一日志标签别名(360->360kan, imgo->mango)

效果:新增源只需在 registry.js 加一行配置,所有分发链路自动查表,零改动。
dandan-api.js 净减 149 行(-225/+76),23 个源的 if/else 分发链全部消除。

* 修改小版本号
2026-08-30 11:15:42 +08:00
huangxd- f77b6b9328 pr_agent增加custom_model_max_tokens 2026-08-30 09:52:35 +08:00
huangxd- a95da3ab6c ci: 接入 PR Agent 自动审查与 PR 描述生成
- 新增 GitHub Actions workflow:在 PR opened/reopened/ready_for_review/synchronize
  及 issue_comment 事件时运行 PR Agent,仅主仓库生效并跳过 Bot 触发
- 默认开启 auto_review / auto_describe,模型与 API Base 通过
  OPENAI_KEY secret 及 OPENAI_API_BASE、PR_AGENT_MODEL 仓库变量注入
- 新增 .pr_agent.toml:回复语言设为 zh-CN,忽略自动化 PR
  (dependabot[bot]、^Auto 标题、dependencies/skip-review 标签)
- 自定义中文审查要点:正确性/安全性/稳定性/可维护性、边界与异常输入、
  并发竞态、性能瓶颈、环境变量与新增文件的必要性、README 是否同步
- review 与 code suggestions 各输出 4 条,建议优先给出可直接采纳的代码修改
2026-08-30 09:41:16 +08:00
xlmcandxlmc 3ffdd13763 feat: add configurable danmaku gradient colors (default off) (#458)
* feat: add configurable danmaku gradient colors

* feat: default GRADIENT_CHANCE to 0 (off)

---------

Co-authored-by: xlmc <xlmc@users.noreply.github.com>
2026-08-25 23:02:47 +08:00
huangxd- c1905b814b 修复红果源并添加批量编辑映射项能力 (#460)
* feat(hongguo): 增强剧集时长获取与播放链接解析

- 新增 parseWebPlayerInfo 解析视频时长,提升详情页剧集时长准确性
- 优化 resolveEpisodeInfo 支持嵌套编码及多次解码,增强兼容性
- 添加 durationCache 缓存,减少重复请求
- 升级版本号至 1.20.9

* feat(ui): 为映射配置添加批量输入和解析功能

新增批量编辑映射项的能力,支持通过分隔符(分号或换行)一次性输入多个“原值->映射值”对,并自动解析更新列表。同时保持手动增删改与批量输入的双向同步,提升配置效率。新增解析校验与日志提醒,避免无效条目被忽略。

* fix(hongguo): 修复弹幕触发风控问题,解析频控冷却退避并限制弹幕请求速率

- 解析服务端 code=100 返回的 min_ban_time/max_ban_time,遵守冷却时间退避后重试,不再直接失败
- 弹幕请求增加节流(COMMENT_THROTTLE_MS + 随机抖动),停止分页/整剧连发
- 弹幕并发从 30 降至 3,降低整剧拉取对接口的压力

* fix: 优化红果剧集/弹幕抓取逻辑(API 优先+并发去重)并修复红果链接分发回归
2026-08-25 23:01:23 +08:00
Elegy233andhuangxd- b06d40999e fix: 修复手动链接搜索与直连接口未剥离 @偏移 后缀导致请求失败 (#455)
* fix: 修复手动链接搜索与直连接口未正确处理 @偏移 后缀

搜索阶段取标题前剥离 @偏移 后缀:此前带 @偏移 的链接直接传入源请求(如 bilibili 返回 404)并产生脏标题,现统一用 stripLinkOffset 取洁净链接后再取标题。

直连接口 getCommentByUrl 此前完全忽略 @偏移,现与 getComment 对齐:解析 @秒数/@%百分比 偏移、用洁净链接取弹幕、取回后应用偏移;百分比偏移按弹幕最大时间缩放,逻辑与 getComment 一致。

将散落各处的 @偏移 解析统一收口到 offset-util.js 的 stripLinkOffset(消除 7 处重复内联正则),getComment / fetchMergedComments / resolveSourceAndRealId / getCommentByUrl 均复用之。

顺带修复:animeko/bahamut 此前用仅匹配 @秒数 的正则,会静默丢弃 @% 百分比偏移,现经 stripLinkOffset 统一解析后予以保留。偏移为 0(如 @0)时不再回写 @0 后缀(原会保留,属无操作偏移,无实际影响)。

* fix: 修复合并链接偏移被重复施加导致弹幕时间整体偏移

合并链接(含 $$$ 分隔的多个子链接)的弹幕在 fetchMergedComments 中已按各子链接来源分别施加偏移,而 getComment 顶层又对整段合并弹幕再次施加单链接偏移,导致所有弹幕时间被叠加偏移一次(首个子链接无偏移标记时其弹幕被整体抬走)。

改为顶层单链接偏移仅在非合并链接时施加,与既有 danmuOffsetRules 守卫(dandan-api.js:2699)一致——合并链接的偏移完全由 fetchMergedComments 按来源处理。单链接偏移行为与直连接口 getCommentByUrl 不受影响。

* fix: 修复合并链接弹幕时长仅取单视频时长而非合并时间轴长度

合并链接(含 $$$ 分隔的多个子链接)此前在 resolveMergedDuration 中对各子链接时长取最大值,导致返回的 videoDuration 为单个视频的时长(如 3266s),而非合并后时间轴的实际长度(约 6463s),前端进度条与时长显示偏短。

改为:合并链接的时长取各子链接按 @偏移 平移后时间轴末端的最大值——即 max(子链接时长 + 应用的偏移)。偏移量直接复用 applyOffset 的语义(绝对与百分比模式下均将时间轴末端平移 offset 秒),确保返回的合并时长与弹幕实际落点完全一致,且不再手算偏移公式。单链接路径保持不变,正常场景行为不受影响。

* test: 在 worker.test.js 活跃区补充 @偏移 解析与 applyOffset 端点语义用例

---------

Co-authored-by: huangxd- <649924757@qq.com>
2026-08-22 12:17:19 +08:00
xlmcandxlmc b5458edd73 feat: 增强弹幕屏蔽词解析与生效诊断日志 (#453)
* feat: 增强弹幕屏蔽词解析与生效诊断日志

- 重写 BLOCKED_WORDS 解析器:支持 /regex/flags 正则词条与纯文本词条混用,兼容中英文逗号(,/,)及逗号前后空格;正则词条内部的逗号不再被误切分
- 正则词条支持 i/m/s/u 等标志;自动剥离 g/y 标志避免 lastIndex 状态污染
- 非法正则自动降级为字面量匹配并输出警告,不再静默丢弃整条规则
- 新增生效诊断日志:规则解析结果、拦截总数/每条规则命中次数、拦截示例、无命中提示、配置无效警告,便于用户确认屏蔽词是否实际生效
- 更新 README 中 BLOCKED_WORDS 说明与示例
- 新增 test_blocked_words.mjs 覆盖 7 类解析场景(7/7 通过)

* fix: 移除未实现的 TITLE_MAPPING_TABLE_URL 配置说明,测试合并至 worker.test.js

- 删除 README 中 TITLE_MAPPING_TABLE_URL / TITLE_MAPPING_TABLE_URL_CACHE_MINUTES 两条配置说明(对应功能未包含在本 PR)
- test_blocked_words.mjs 的 7 类屏蔽词解析场景合并进 danmu_api/worker.test.js,删除独立测试文件(38/38 通过)

---------

Co-authored-by: xlmc <xlmc@users.noreply.github.com>
2026-08-22 12:07:48 +08:00
Elegy233 45e47a5436 fix(animeko): 迁移镜像域名 (#451)
镜像域名已被 DNS 污染,服务迁至新域名,直接替换镜像域名两处(mirrorBase / mirrorUrl)。
2026-08-22 12:07:11 +08:00
Elegy233 70d4f50913 fix: 修复 Bangumi-Data 拆分季条目映射同一源 id 导致重复请求 (#450)
Bangumi-Data 将拆分季番剧(如 Re:0 第四季的丧失篇/夺还篇)拆为多个条目,
但这些条目的源站点 id 相同(如 anidb 均指向 dandan 19242)。searchBangumiData
组装结果时未按源去重,导致源对同一 id 重复请求、产生标题随机的重复条目。

修复:抽出 dedupeBangumiSearchResults,精确匹配优先排序后按 matchedSiteKey:siteId
去重,同源多条目保留标题精确命中检索词的一条、其余标题并入别名;tmdb 不分季且
按标题消费,不参与去重。
2026-08-22 12:06:24 +08:00
Elegy233 4cd15e7b8f fix: 修复合并落单集弹幕请求时 source:id 前缀未剥离导致巴哈 videoSn 拼错 (#449)
合并结果中的落单集(副源独有、无主源对应)URL 采用 source:id 格式(如 bahamut:50709),
但 getComment 的单源分发分支直接把完整 URL 传给源,未剥离 source: 前缀,导致巴哈源
将 "bahamut:50709" 当作 videoSn 请求(videoSn=bahamut:50709),返回 0 条弹幕。

修复:单源分发复用 sanitizeUrl 剥离 source:id 前缀(sanitizeUrl 由 merge-util 导出),
与合并路径 fetchMergedComments 的分割逻辑一致。
2026-08-22 12:06:02 +08:00
huangxd- 12d62ad97f 修复AI匹配中单集电影抢占SxxExx电视剧的问题及红果弹幕获取 (#454)
* feat: 添加收藏功能可用性检测,未配置Redis时禁用UI操作

- 在收藏列表接口中返回 `favoriteSupported` 字段,根据部署平台及 Redis 配置动态判断
- 前端根据 `favoriteSupported` 禁用“收藏”按钮,并显示配置 Redis 的提示信息
- 更新 README 说明 serverless 平台需配置 Upstash Redis 才能启用收藏功能
- 避免无持久化存储的实例在冷启动或实例切换后丢失收藏数据导致用户误操作

* chore: 升级版本号至 v1.20.8

* fix: 修复AI匹配中单集电影抢占SxxExx电视剧的问题

- 新增 isMovieMatchCandidate、getMatchEpisodeCount 和 isSingleEpisodeMatchCandidate 函数,用于准确识别电影及单集候选。
- 在 matchAniAndEpByAi 中,若 AI 选中单集候选而存在多集电视剧候选,则回退到常规季集匹配,避免同名电影错误命中 S01E01。
- 在常规匹配循环中,跳过单集候选参与有季集参数的匹配,并调整评分逻辑,对多集候选加分、单集候选减分,提升多集电视剧优先级。
- 优化 break 条件,避免单集候选提前终止搜索,确保多集候选能被充分比较。

* feat(hongguo): 添加Web端HTML解析作为搜索和剧集获取的备用方式

- 新增parseWebSearchResults和parseWebSeriesDetail函数,从HTML响应中提取JSON数据
- search方法优先请求Web端/search页面,解析搜索结果列表
- getEpisodes方法优先请求Web端/detail页面,解析剧集列表
- 当Web请求失败时回退至原有API方式,提升数据获取的稳定性
2026-08-22 12:05:38 +08:00
huangxd- 1bdb2a879e fix: 修复 widget 构建中 http/https 模块兼容性及环境检测逻辑
- 在构建插件中为 node:http 和 node:https 添加解析规则,并提供空实现,避免构建失败
- 增加 IS_NODE_RUNTIME 判断,结合 __FORWARD_WIDGET__ 标识和 process.versions.node 准确识别 Node 环境
- 修正降级检测逻辑,仅在 Node 运行时检查版本,防止在浏览器环境下误判并触发不必要的 node-fetch 降级
- node-fetch Agent 仅在 Node 运行时创建,避免在非 Node 环境引用 http/https 模块导致异常
- 调整全局变量定义,注入 __FORWARD_WIDGET__ 标识供运行时环境判断使用
2026-08-15 08:48:08 +08:00
huangxd- e21bd99677 docs: remove recommended label for Netlify deployment
移除 Netlify 部署方式的“【推荐】”标签,因为该平台存在封号风险,不再适合作为推荐选项
2026-08-15 08:10:28 +08:00
Elegy233andhuangxd- 9bfca7a8e9 fix(bangumi-data): 修复 Bangumi Data 开启可能不触发立即下载,并调整云部署数据丢失时下载时机 (#444)
* fix: Bangumi Data 按需下载下沉至消费路径并修复配置变更未触发下载与漏写 await

将 Bangumi Data 初始化从 worker.js 网关层整体移除,下沉为 searchBangumiData
消费路径上的按需下载触发(ensureBangumiDataReady):仅在真正读取缓存的路径
(4 源搜索/匹配/弹幕 + 全局跨季边界)触发,未配置消费源的部署(如仅 renren)
不会下载,云部署丢失内存时由数据请求重新触发。

新增 syncBangumiDataLifecycleOnConfigChange,在 USE_BANGUMI_DATA 由关到开时
立即触发一次下载、由开到关时释放缓存;接入 env-api 三处 handler 与 server.js
的 .env 监听器,覆盖前端保存与本地直接改文件两种启用路径。

修复 tmdb-util.js getTMDBChineseTitle 调用 searchBangumiData 漏写 await,使本地
Bangumi Data 中文名命中路径真实生效(此前 localMatches 为非同步 Promise、命中
恒不生效)。

移除无调用方的 ctx 参数(handleRequest 与 initBangumiData):上游在 fetch 边缘层
经数据层 ctx.waitUntil 延长后台下载,本分支将下载触发下沉至消费路径后不再深穿 ctx,
该参数及依赖它的 ctx.waitUntil 分支已无调用来源,移除以避免无调用来源的防御性代码。

* fix: 暴露 Bangumi Data 在途后台下载并由边缘运行时延长生命周期

下载触发下沉至消费路径后不再将 ctx 深穿至数据层,上游原本由数据层 ctx.waitUntil
延长的后台下载在边缘层失去接线,导致边缘 isolate 在响应返回后冻结会中断在途的
后台静默下载(仅影响刷新效率,不阻塞请求,靠幽灵死锁自愈兜底)。本提交改为在边缘层
暴露在途下载并延长生命周期:

- initBangumiData 发起非阻塞刷新时把在途下载 Promise 写入模块级 currentBackgroundDownload,
  下载完成即在 finally 中复位;新增 getBackgroundDownload 暴露该状态。
- 新增 extendBangumiDownloadLifecycle(ctx),仅在存在在途下载且运行时提供 waitUntil 时
  注册,供边缘层在响应返回后延长 isolate 生命周期以容纳下载;无 ctx 的运行时自然 no-op。
- worker.js 的 Cloudflare Workers fetch 入口重新接收 ctx 参数,await handleRequest 后调用
  extendBangumiDownloadLifecycle(ctx);vercel/netlify 入口无 ctx,不受影响。
- 集中下载启动逻辑为 startDownload,三处刷新分支复用,消除重复的锁与复位代码。

* chore: Bangumi Data 模块级 let 声明整理为末尾注释并合并分组空行

将 currentBackgroundDownload、activeDataSource、cachedCustomVersion 的独立注释
行改为声明末尾注释,与既有的 versionQueryPromise 风格统一;删除 data-source 组内
cachedOfficialVersion 与 versionQueryPromise 之间的多余空行,保留 cache 组与
data-source 组之间的逻辑分隔空行,使模块顶部状态声明更紧凑一致。

* fix: 补全 bangumi 测试注释块缺失的导入符号

测试注释块第一组的 bangumi-data-util 导入仅含 ensureBangumiDataReady 与
syncBangumiDataLifecycleOnConfigChange,但同组用例还使用了 initBangumiData 与
clearBangumiDataCache,解除注释后会因未导入而 ReferenceError。getTMDBChineseTitle 与
assert 已由 worker.test.js 顶部全局导入,无需重复。补全缺失的两个符号,使该注释块
解除注释即可经 node --test 运行,并已在沙箱真实下载下验证通过。

---------

Co-authored-by: huangxd- <649924757@qq.com>
2026-08-15 08:06:38 +08:00
Elegy233andhuangxd- 7e5c8073e0 fix: 自定义文本类环境变量读取绕过 dotenv 注释截断以保留 # (#443)
问题:dotenv.config() 将未加引号的行内 # 视为注释起始,导致自定义文本类变量中合法包含的 # 被截断、规则失效;即便用户通过前端写入双引号可规避,直接编辑 .env 不加引号时仍会触发。

方案:在 Envs.get 中对未加密的文本类自定义变量(RAW_ENV_KEYS)统一路由到 getRawEnv,直接解析 .env 原始文本(仅跳过整行 # 注释、剥离外层双引号),从而保留 # 等字符。优先级保持系统环境变量 > .env 原始值 > 默认值,与既有 systemEnvBackup 机制一致;非 Node 运行时(测试 / Workers)无原始 .env 时退化为普通取值语义,不影响测试隔离与平台兼容。前端设置页读取的 originalEnvVars 同样由 get/getRawEnv 写入,显示侧一并修复。同步移除 resolveCustomMergeRules 中已冗余的 accessedEnvVars.set 调用。

覆盖范围:RAW_ENV_KEYS 含合并映射表、标题/自动匹配映射表、弹幕偏移、屏蔽词、各标题/剧集噪音过滤器、IP 黑名单、AI 匹配提示,以及非加密的 COLOR_POOL / DANMU_PUSH_URL / OTHER_SERVER / VOD_SERVERS(URL / 颜色类)。

加密守卫:get() 路由条件加 !encrypt,防止加密变量(PROXY_URL / CUSTOM_SOURCE_API_URL / UPSTASH_REDIS_REST_URL / LOCAL_REDIS_URL 等带令牌 URL)被绕过加密返回明文;此类加密变量刻意不入 RAW_ENV_KEYS(其 # 风险极低且经安全写入路径)。

测试:worker.test.js 增加对应注释测试桩(按项目约定注释待启用);独立的 raw-env-read.test.mjs 仅本地验证、不入库。

Co-authored-by: huangxd- <649924757@qq.com>
2026-08-15 08:01:47 +08:00
Elegy233 00efe4a815 feat: 清理缓存支持按项勾选清理 (#441)
* feat: 清理缓存支持按项勾选清理

上游原版 handleClearCache() 不区分具体项,调用即一次性重置全部内存缓存(animes、episodeIds、episodeNum、lastSelectMap、searchCache、commentCache、requestHistory 及 Bangumi Data)。

本提交改为按项勾选清理:
- apis/system-api.js:handleClearCache 接收可选请求体 { items: string[] },按 clearActions 映射逐项重置;请求记录(reqRecords)与今日计数(todayReqNum)归入「请求历史记录」项连带清理;未提供请求体或解析失败时回退为全清,兼容旧路由与测试的无参调用。未知键经 key in clearActions 过滤,无注入面。
- worker.js:POST /api/cache/clear 透传请求体。
- ui/template.js:清理缓存弹窗改为 8 项可勾选清单,含全选/全不选与已选计数。
- ui/js/systemsettings.js:新增 selectAllCacheItems、updateCacheClearCount;confirmClearCache 收集勾选项发送 {items},空选拦截。
- ui/css/components.css.js:新增缓存弹窗与通用复选框样式。

收藏(favoriteCache)按上游契约不在清理范围内,worker.test.js「清理缓存保留收藏」断言仍然成立。

* feat: 优化通用复选框样式与偏移设置交互

承接清理缓存弹窗的通用复选框,继续完善前端交互(均基于上游原版):
- ui/css/components.css.js:通用复选框由 accent-color 改为 appearance 自绘,以 --theme-check-color 控制勾选色(浅色恒白、暗色恒黑,规避浅强调色在浅色模式得黑勾);以 body[data-theme] 前缀将特异性提升至 3002,压过主题规则 themes.css.js:191 对 .form-group input 背景与边框的重设(2002),使位于 .form-group 内的偏移百分比复选框与缓存复选框表现一致;补 padding:0 避免继承 .form-group input 内边距导致长方形。
- ui/css/themes.css.js:浅色与暗色模式分别定义 --theme-check-color。
- ui/js/systemsettings.js:偏移百分比复选框标签去除 white-space:nowrap,窄屏长文案可换行,复选框不被压缩。

* test: 新增 handleClearCache 按项清理测试

覆盖 handleClearCache 的全部关键路径:
- 按项选清:单选一项只清该项,其余缓存保留
- 非法键过滤:不在 clearActions 的键被忽略,不触发任何清理动作
- requestHistory 项同时清空 reqRecords 并重置 todayReqNum 为 0
- episodeNum 重置为 10001
- 全清(无 req 或畸形 body 非数组)保留 favorites
- 空数组 items 不清理任何项
- bangumiData 作为合法键被识别且独立清理

全部子测试在 node --test 下通过,整文件 36/36 通过。

* fix: 修复清理缓存原型键误放行并优化弹窗提示与计数样式

清理缓存功能:修复 handleClearCache 的原型键过滤缺陷,并优化弹窗提示与计数视觉。

- apis/system-api.js:handleClearCache 的 items 过滤由 'key in clearActions' 改为 Object.prototype.hasOwnProperty.call(clearActions, key),排除 __proto__/constructor 等原型链键误放行导致整次清理失败(success:false)。
- ui/template.js:提示文案改为「请勾选需要清理的缓存项:」,采用上游风格的全可见度清晰提示。
- ui/css/components.css.js:.cache-clear-hint 字号 13px 增至 16px、颜色由灰改为正文色,与全选/全不选按钮形成清晰层级;.cache-clear-count 增加淡中性全圆角状态标签底色,平衡左右视觉重量且刻意不像按钮。
- worker.test.js:新增原型键回归测试,固化上述修复。

* fix: 统一清理缓存项标签大小写并调整项顺序

清理缓存弹窗:统一缓存项标签写法并调整为预期顺序。

- ui/template.js:动画元数据缓存标签由「动画元数据缓存 (Bangumi Data)」改为「动画元数据缓存 (bangumiData)」,与另 7 项括号展示真实键名的写法(animes / episodeIds / episodeNum / lastSelectMap)统一,便于 UI 与代码键名对照。
- 清理项顺序调整为:搜索结果缓存、弹幕内容缓存、请求历史记录、动漫搜索缓存、动画元数据缓存、剧集ID缓存、剧集编号缓存、最后选择映射缓存。
- 各 checkbox 的 value 与 handleClearCache 的 clearActions 键名一致,无功能影响;仓库其余「Bangumi Data」均为数据源专有名词,不涉及此改动。
2026-08-15 07:58:50 +08:00
Elegy233 65ab6d0658 fix(ui): UI 细节与响应式优化 (#440)
* fix(ui): 七项细节打磨 + 两项响应式优化

- 配置错误 banner 迁移至主题变量,支持暗浅色切换
- custom-alert 访问方式前换行,长 URL 添加 word-break 防溢出
- applyInputFeedback 移除 focus() 避免快捷填充时视点跳转
- map-remove-btn hover rotate→translateY,修复删除按钮旋转
- 手动匹配收藏按钮移至搜索按钮左侧
- episode-item flex 布局+hover 背景+左边框 accent,改善辨识度
- 弹幕词库新增 @wade6716,「大家陪你看」→「弹幕陪你看」
- 响应式:搜索栏手机端输入独占上行、收藏搜索下行
- 响应式:剧集按钮三段宽度 PC:120px、平板:160px、手机:100px

* fix(ui): 关闭移动端点按高亮以避免默认蓝色闪现

根因:移动端点击任意可点元素出现的蓝色方框/闪现来自浏览器默认的 -webkit-tap-highlight-color(点按高亮),属 WebKit/Blink 触摸专属机制,PC 端不触发,故表现为仅手机端出现。项目此前未设置该属性,一直沿用 UA 默认蓝色高亮。

该高亮并非无障碍焦点轮廓:键盘焦点由项目既有 :focus-visible 规则提供,仅键盘聚焦生效、鼠标/触摸点按不触发,PC 与手机行为一致;移除点按高亮不会损害键盘导航。

修复:在全局重置的 * 选择器中加入 -webkit-tap-highlight-color: transparent,一次性覆盖所有可点元素。按钮自身已具备 :active 按压反馈(.btn:active / .btn-primary:active / .nav-btn:active / .number-btn:active),触摸按压反馈保留。

* fix(ui): 修复 customAlert 使用 innerHTML 导致的 XSS 漏洞

customAlert 此前通过 messageElement.innerHTML = message.replace(/\n/g, '<br>') 渲染提示消息,会执行 message 中的任意 HTML。其调用方大量拼接不可信内容(error.message、接口返回的 result.message、配置校验 message 等),上游或代理响应若含 <script> 或 <img onerror> 即触发 XSS,可窃取管理页 ADMIN_TOKEN 与 localStorage。

修复方式:改用 messageElement.textContent = message 使 HTML 转义为纯文本;换行需求由 #custom-alert-message 元素的 white-space: pre-line 经 CSS 渲染,与原有 <br> 视觉一致。该元素同时被 customConfirm 复用,两者行为统一。

枚举全部 customAlert/customConfirm 调用点确认:仅含换行符的两条消息(访问方式提示、导入失败项)依赖换行,其余消息均不含换行符,pre-line 对其渲染与 white-space: normal 完全一致,不对其它文字造成负面影响。

* fix: 修复跳转输入框占位符居中、窄屏溢出与字号对齐

手动匹配测试与推送弹幕测试的跳转工具:修复「输入集数」输入框的视觉问题。

- ui/css/components.css.js:.jump-episode-input 新增 max-width:100% 与 min-width:0,使极窄屏下输入框可收缩而不顶出视图;text-align:center 使占位符「输入集数」水平居中。
- 新增 .jump-to-episode > span { font-size:13px },将「跳转到第」「集」提示文字与输入框字号统一(原继承浏览器默认 16px),整行视觉真正对齐。
- 该 input 类仅挂在两个跳转输入框上,text-align 不影响其他文字或容器。

* fix: 统一跳转输入框提示文字字号实现整行视觉对齐

ui/css/components.css.js:补充 .jump-to-episode > span { font-size:13px },将「跳转到第」「集」提示文字与输入框字号统一(原继承浏览器默认 16px),整行视觉真正居中。仅此一处规则,其他跳转框改动已在 e5b2c90 推送。

* fix(ui): 最近数据面板分页展示、标题换行与封面防盗链修复

- 最近数据改为分页渲染,默认展示 5 条,底部加载更多按钮逐步展开
- 番剧标题去除单行省略,改为换行完整显示
- 封面由 background-image 改为 img 并加 referrerpolicy="no-referrer" 规避源站防盗链
- 封面 URL 经 escapeHtml 转义以防 XSS

* fix(ui): 子源标题换行与最近数据面板 XSS 修复

- 被合并子源标题去除单行省略,与主标题一致换行完整显示
- 番剧标题、来源名、剧集链接标题统一经 escapeHtml 转义,修复 innerHTML 注入
- 快捷填入按钮由内联 onclick 改为 data-* 属性 + 事件委托,修复 onclick JS 注入
2026-08-15 07:58:02 +08:00
Elegy233 62fd0afddf feat: 弹弹play源支持经 NipaPlay 302关联链接获取弹幕,并支持 NIPAPLAY_REPLACE_DANDAN 替代原生弹幕开关 (#439)
* feat: 弹弹play源支持经 NipaPlay 302关联链接获取弹幕,并支持 NIPAPLAY_REPLACE_DANDAN 替代原生弹幕开关

当 dandanplay 原生弹幕为空(新番上线缓存空窗期)时,dandan 源借助 NipaPlay 授权的
302关联接口,经 302 重定向获取用户在弹弹play客户端实际绑定的跨平台播放链接,实时拉取
bilibili/bahamut/iqiyi/youku/tencent/mango 的弹幕作为兜底。

新增 NIPAPLAY_REPLACE_DANDAN 开关(默认关闭,经 envs 暴露、前端可配置):开启后 dandan
源以 nipaplay 关联弹幕替代弹弹原生弹幕,跳过原本的弹弹 HTTP 弹幕接口请求(不做并行,避免请求量
放大);当关联链接为空时回退弹弹自身弹幕作为安全网(因采用项目链路获取弹幕,会丢失弹弹
自身平台弹幕,多平台中巴哈需确保可连通)。

实现要点:
- 关联接口凭证由 NipaPlay 授权下发,参数经运行时派生与重组还原,源码不出现可读明文(禁止滥用);
- 签名生成、302 关联链接的解析与请求抽取至 nipaplay-util.js,dandan.js 仅负责触发与分发;
- 复用各平台既有源实例拉取弹幕,对各源零侵入;
- 兜底弹幕按真实来源标注平台标识(如 bilibili1/qq/qiyi),与手动解析链接输出一致;
- 同平台多个链接串行、间隔 1 秒请求,避免短时间内同平台并发触发风控;
- 已独立选择的合并源按源名与平台标识双重比对跳过,避免重复获取;对跳过的合并源输出日志,使合并源冲突防范可见;
- 关联接口响应仅含重定向(无弹幕体,单次线上响应约数百字节),弹幕数据来自后续按平台
  链接实时拉取,对既有请求路径无额外放大。

* test: 增加 nipaplay 弹蛋302关联工具函数单元测试(注释态)

覆盖 parseNipaplayRelatedLinks、resolveNipaplayLink、applyShiftToDanmu、generateNipaplaySignature;按仓库既有约定以注释形式提交,解除注释即可经 node --test 运行。

* fix: 移除 NIPAPLAY_REPLACE_DANDAN 配置说明反引号并改用换行

envs.js 与 .env.example 的 NIPAPLAY_REPLACE_DANDAN 说明原使用反引号包裹 false/true 与三条注意事项,前端当前无法渲染反引号。改为去掉反引号,并参照既有配置(如 USE_BANGUMI_DATA)使用换行分隔默认值、可选值与注意事项,提升前端展示可读性。

* docs: 调整 README 致谢中 NipaPlay-Reload 弹弹302关联链接描述

去掉致谢行中重复的 NipaPlay-Reload 字样,并将授权改为请求授权以准确描述链接授权性质。

* docs: README 致谢补充弹弹play开放平台链接

在 NipaPlay-Reload 提供的 302关联链接请求授权描述中,为弹弹play开放平台补充官方文档链接,与上方 danmaku-anywhere 行风格一致。
2026-08-15 07:57:46 +08:00
Elegy233 791bf6fb5e fix: 修复多 p 视频按总时长计算分段数导致越界请求 304 空响应 (#438)
普通投稿视频分支使用 data.data.duration(全部分 p 的总时长)计算弹幕分段数,却只对第 p 页的 cid 发起请求。多 p 视频单页时长远小于总时长,按总时长算出的分段数远超该页实际分段,越界请求的分段在 B 站不存在、返回 304 空响应,被熔断逻辑误判为视频结束而中断后续分段获取,造成弹幕缺漏。

改为使用 data.data.pages[p - 1].duration(第 p 页真实时长)计算分段数,与 cid 取值同源;单 p 视频 pages[0].duration 与 data.duration 相等,无回归。
2026-08-15 07:57:24 +08:00
Elegy233 406922157e fix(mango): 综艺纯第N期条目取消后缀过滤,直接作为正片收录 (#437)
纯第N期标题(无上/中/下)经主黑名单预滤后均为正片,
对其做后缀过滤仅造成误杀(如"第8期:海清制作美味"
中的"制作"被误判为特辑)。芒果TV首页印证:所有filler
内容(加更版/超前企划等)均不使用第N期格式。
上/中/下格式保留原有后缀过滤逻辑不变。
2026-08-15 07:57:05 +08:00
Wade 66fb2de8b0 fix(match): 修复偏好偏移计算目标集数为0时数组越界引发TypeError及400异常 (#436)
- 修复  在  时访问  导致  漏洞;
- 修复  在  时访问  导致的越界异常;
- 优化匹配处理逻辑的最外层 catch 错误日志,记录完整 error 堆栈并输出明确的错误信息。
2026-08-15 07:56:48 +08:00
huangxd- 0d3f7ede7c feat: 支持 IPv4/IPv6 双栈监听并优化服务器启动与清理逻辑 (#445)
- 新增 server-listen-util.js 工具,实现 IPv4/IPv6 双栈监听及 IPv4 自动回退
- server.js 改用 listenOnAllInterfaces 替代硬编码 0.0.0.0,提升网络兼容性
- 优化 cleanupWatcher 函数,增加 listening 检查及退出码参数,避免重复关闭
- README 补充 IPv6 访问格式、Docker 双栈说明及相关工具文件描述
- 版本号升级至 1.20.7
2026-08-15 07:56:27 +08:00
lilixu3 792bb5aefb feat(ui): 完善弹幕详情全格式导出、来源操作与内容筛选 (#433)
- 支持选择后端全部弹幕格式导出,正确处理 JSON、XML 与二进制响应
- 自动和手动匹配结果均透传并展示来源地址,提供复制与打开操作
- 增加弹幕内容关键词搜索,并可与滚动、顶部、底部类型筛选组合使用
- 将首次加载及每次加载更多数量从 100 调整为 500
- 补充桌面端与移动端响应式布局,避免控件挤压和横向溢出
2026-08-11 00:19:52 +08:00
Elegy233 89fd166424 fix: 旧版 Node 与 iOS 环境下请求降级至 node-fetch v3 修复响应头丢失导致个别源弹幕获取失败 (#431)
* fix: 旧版 Node 下改用 node-fetch v3 以修复 Set-Cookie 被丢弃

Node 18 自带的 undici(fetch 实验特性)在解析响应头时会丢弃 Set-Cookie,导致 youku 等依赖该响应头做令牌握手的请求在 Node < 20.19.0 下失败(FAIL_SYS_TOKEN_EMPTY、segmentList is not iterable)。

将请求层(http-util.js)原仅针对 iOS 巨魔环境的 node-fetch v3 降级条件扩展到「Node < 20.19.0」,与 esm-shim 兼容性边界 20.19.0 对齐;并在 server.js 启动处预加载 node-fetch v3,确保 esm-shim 的 require 代理可被同步取用。httpGet / httpPost / httpRequestMethod / httpGetWithStreamCheck 四处 fetch 调用点统一经 shouldUseNodeFetch() 判定,Node >= 20.19.0 仍走原生 fetch,行为与修复前一致。

* 优化: 旧版Node/iOS降级路径启用 keep-alive 连接复用并收敛降级日志为单次

旧版 Node / iOS 环境下统一降级到 node-fetch v3 后,node-fetch 默认走 keepAlive:false 的全局 Agent,并发场景(如跨季并行 mapping)下比原生 undici 连接池更重。本提交在降级分支接入共享 keep-alive Agent(按协议区分 https/http),复用 TCP/TLS 连接,与原生 undici 连接池达到实际等价。

同时将原本 4 个 fetch 调用点「每次请求都输出」的降级日志收敛为模块加载时计算一次、运行时单次启动日志,避免降级环境下日志刷屏。降级判定仅依赖静态环境、进程内恒定,故可安全缓存。

* 文档: 精简 http-util 降级判定的函数注释

将 detectNodeFetchDowngrade 上方注释压缩为单行,保留降级边界(Node<20.19.0 与 esm-shim 一致)、根因(旧 undici 丢弃 Set-Cookie)、iOS 无 WebAssembly 及静态判定缓存动机,去除冗余推导表述。
2026-08-11 00:19:24 +08:00
huangxd- 17b3f4d723 fix: 优化自动匹配映射表开放规则排序,同源季下按起始集数降序优先 (#435)
调整 `resolveAutoMatchMapping` 中开放规则的排序逻辑:当多个开放规则指向同一源标题和季数时,优先匹配起始集数更大的规则(即更具体的区间),仅在相同起始集数时保持声明顺序。这解决了同季多个映射段重叠时规则选择错误的问题,并更新 README 说明文档,新增对应单元测试覆盖该场景。
2026-08-11 00:19:02 +08:00
huangxd- 1b719f43d6 feat: 新增收藏管理权限控制环境变量 FAVORITE_REQUIRE_ADMIN (#434)
* feat: 新增收藏管理权限控制环境变量 FAVORITE_REQUIRE_ADMIN

支持通过环境变量控制收藏写入和管理接口是否必须使用 ADMIN_TOKEN,收藏列表仍保持公开只读。更新了配置示例、环境变量文档、UI 预览及版本号至 1.20.6。

* fix: 修复排序配置中源重复选择问题并优化合并模式逻辑

- 对 SOURCE_ORDER 和 PLATFORM_ORDER 的已选项按 `&` 拆分后判断源是否已存在,避免同一源重复添加
- MERGE_SOURCE_PAIRS 保留同一源参与不同合并组的能力,仅检查暂存区重复
- 将禁用状态检查提前至 addSelectedTag,防止无效添加
- 新增单元测试覆盖拆分逻辑和重复源防护
2026-08-11 00:18:37 +08:00