Commit Graph
431 Commits
Author SHA1 Message Date
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
lilixu3 72e345e840 fix: 兼容不支持 Unicode 属性正则的运行时 (#432)5 2026-08-09 13:21:42 +08:00
Elegy233 251d6f93b9 feat: 使用 bangumi-data 数据实现季边界映射,优化跨季请求量与准确性 (#429)
* feat: 使用 bangumi-data 数据实现季边界映射,优化跨季请求量与准确性

本提交聚焦于跨季扩展的「请求量」与「命中准确性」两项优化,并以请求级 HTTP 缓存统一解决跨季扩展产生的重复请求(全源受益、零源改动)。

一、TMDB 季边界映射(核心能力,新增 tmdb-util.js)
- 新增 buildTmdbSeasonBoundaries / getTmdbSeasonBoundaries,消费 bangumi-data(searchBangumiData)的扁平结果 {matchedSiteKey, siteId},将 TMDB 的季结构映射为本项目的「季序 → 起始集」边界表。
- dandan-api.js 跨季扩展:在启用 Bangumi Data 且能解析到 TMDB 季边界时,由原先「向后逐季顺序扩展(S+1、S+2…)」收敛为「依据 TMDB 边界直接定位目标集所在季」,跳过其间无关季(如要看 S5、中间 S4 无需拉取),降低无效 API 请求量;未启用 Bangumi Data 或边界未解析时,维持上游原版逐季顺序扩展。

上述 TMDB 季边界增强仅在启用 Bangumi Data 且成功解析到边界时生效;否则跨季扩展与顺延均回落到上游原版逻辑。

二、跨季顺延准确性修正
- 跨季顺延的集数扣减:在能解析到 TMDB 边界时按「TMDB 标准总集数 seasonEpisodeTotal」扣减,否则按原版「某季实际拉到的集数 allEps.length(可能偏短或偏长)」扣减,避免中间季集数统计偏差导致收敛到错误季。
- 原 [spillover] 共有…集 日志同步改用 seasonEpisodeTotal 以与扣减一致。

三、请求级 HTTP 响应复用(全源去重,零源改动)
- http-util.js 的 httpGet 新增请求级缓存:同一搜索请求内相同 URL 的重复 GET 直接复用首次响应,不再发网络请求;配套 httpCacheContext 与 runWithHttpCache。
- dandan-api.js 的 searchAnime 用 runWithHttpCache 把整次搜索(主搜索 + 跨季扩展)包进同一请求级缓存上下文。
- 跨季扩展重跑各源 handleAnimes / getEpisodes 时,使用的是与主搜索相同的番剧 id(详情 URL 一致),重复 GET 自动抹平。该机制位于共享 httpGet,对所有约 20 个源自动生效,无需改动任何源文件。

四、改动范围最小化
- 最终仅 3 个文件有分支专属改动:dandan-api.js(跨季扩展逻辑)、tmdb-util.js(季边界映射)、http-util.js(HTTP 缓存)。各源文件(含 dandan.js)经评审已全部回退至上游,未携带任何分支专属改动。

* fix: 修复请求级 HTTP 缓存误杀源级同 URL 重试

请求级 HTTP 缓存(httpCacheContext / runWithHttpCache)对每次成功响应写缓存,读取位于 httpGet 顶部、先于源级重试逻辑。iqiyi 搜索风控(code=-1)与 base_info 异常的源级同 URL 重试重调 httpGet 时命中缓存,复用首次已缓存的失败响应,导致重试被静默吞掉、始终失败。

新增 bypassCache 选项:源级重试显式声明后跳过读/写缓存,刻意重取绕过去重;首次(非重试)调用仍走缓存,跨季扩展的全源去重收益保留。通用请求工具内置的 retries: 重试不受此问题影响(失败尝试不写缓存、循环内重发网络)。本修复仅 iqiyi 有源级同 URL 重试,故根机制(http-util.js)配合其重试接线即覆盖全部受影响源。
2026-08-09 12:23:59 +08:00
Elegy233 115fae378b fix: 修复 PLATFORM_ORDER 平台优先递进失效 (#427)
PLATFORM_ORDER 配置多个平台时,预期按序递进:qq 优选未匹配则顺延 qiyi→youku…,依次类推。实际表现为首个标题命中但平台得分 0 的番剧被误当作当前平台匹配,导致后续平台无法递进。

根因:matchAniAndEp(title,…,platform,…) 仅将 platform 参数用于评分,从不作为硬接受条件;而上层 for (const platform of dynamicPlatformOrder) 一旦取到任意 truthy 的 resAnime 即 break。二者契约错位,使首个标题命中、但平台不匹配(得分 0)的番剧(如 dandan&animeko 对 qq)被当成该平台匹配,阻断了后续平台递进。日志中 Found match with platform: qq 打印的实为循环变量而非真实平台,正是误导来源。

修复:在 matchAniAndEp 返回前增加守卫——指定平台偏好且最佳结果得分 <= 0 时返回空匹配,交由上层按 PLATFORM_ORDER 顺延到下一平台;全序耗尽则回退默认匹配(首个满足条件的番剧),契合「两者都没有则返回第一个满足条件的平台」。

影响范围:仅收紧了「指定平台偏好」时的接受条件;手动优选(isPreferredAnime +9999 绝对优先)、未配置 PLATFORM_ORDER 时的默认行为均不变。
2026-08-09 12:23:22 +08:00
huangxd- b811f8fd17 feat(match): 支持自动匹配映射表规则 (#430)
* feat(favorite): 重构收藏匹配逻辑,为搜索场景独立实现解析函数

- 将原 `resolveFavoriteForKeyword` 重命名为 `resolveFavoriteForSearchKeyword`,专门用于搜索场景的收藏命中判断
- 新增 `resolveFavoriteForSearchKeyword` 实现精确标题匹配,避免短关键词误复用更长标题的收藏结果
- 更新所有调用点(缓存有效性、搜索接口、匹配接口)使用新函数
- 版本号升级至 v1.20.5

此变更解决了因关键词长度差异导致收藏缓存错误命中其他剧集的问题,确保搜索缓存和收藏优先级判断更准确。

* feat(ui): 将弹幕测试设为接口调试页面的默认标签页

交换了顶部标签按钮的顺序,并将对应的内容区域设为激活状态,使页面加载时默认显示弹幕测试功能,提升测试场景的使用便捷性。

* feat(match): 支持自动匹配映射表规则,可跨季映射并指定平台/结果优先级

- 新增 `AUTO_MATCH_MAPPING_TABLE` 环境变量,支持开放映射(源季递增)和有限范围映射(等长范围),并支持目标结果优选(含年份/类型/平台)
- 更新 README 和 `.env.example` 中的配置说明,明确映射优先级为:当前源季手动偏好 > 本映射表 > `TITLE_MAPPING_TABLE` > 普通匹配
- 优化 `REMEMBER_LAST_SELECT` 描述,澄清仅记录明确手动选择,自动匹配后直接返回不会写入偏好

* feat: 预览数组值显示为逗号分隔字符串

在预览界面中,当数据字段为数组类型时,原先会进入对象处理逻辑导致显示不友好。
现增加对数组类型的单独处理,将其转换为逗号分隔的字符串,提升预览可读性。

* fix: 修复标签拖拽触摸支持并清理历史脏数据

- 为表单控件添加 touch-action 样式,改善移动端交互体验
- 关闭模态框时主动取消触摸拖拽状态,防止事件残留
- 选中值渲染时对逗号分隔项进行去重,清理历史重复脏数据
- 重构标签元素获取逻辑,统一排除拖拽幽灵元素
- 为待选标签添加 touchcancel 事件处理,完善触摸拖拽生命周期
- 实现基于坐标的选中标签拖拽目标检测,提升拖拽定位准确性
- 拖拽结束后清空内部引用,避免内存泄漏
2026-08-09 12:23:03 +08:00
huangxd- 838b5685ac feat: 添加永久收藏搜索结果缓存功能 (#426)
* feat: 添加永久收藏缓存功能,支持手动收藏/刷新/删除

- 新增 `/api/v2/favorite/add`、`/list`、`/refresh`、`/remove` 接口,用于管理永久收藏的搜索关键词及整组搜索结果
- 搜索时若命中收藏缓存(含包含关系匹配)则直接返回,不再请求外部弹幕源,提升重复搜索效率
- 收藏缓存无 TTL 和数量上限,Node/Docker 部署可持久化至 `.cache/favoritesCache`,serverless 仅内存保留
- 更新 README 文档,补充收藏功能说明、API 用法及 UI 操作指引
- 扩展搜索函数支持 `forceRefresh` 参数,刷新时绕过所有缓存重新查询
- 同步调整普通搜索缓存逻辑,确保收藏优先且不受常规缓存过期清理影响

* feat: 完善 Redis 持久化支持,收藏和查询信息可跨实例恢复

- 更新文档,明确 Redis 仅持久化原有查询信息和收藏缓存,搜索结果与弹幕缓存仍仅存于内存
- 收藏操作后同步写入 Redis,避免 serverless 冷启动丢失收藏
- 修复 `favoriteCache` 在 Redis 中的序列化问题,支持 Map 类型正确转换
- 优化 Redis 缓存更新逻辑,确保收藏与查询信息一致持久化

* feat: 添加收藏定时刷新功能

新增 `/api/v2/favorite/schedule` 接口,支持为收藏设置每日或每周定时刷新(需指定时间和星期),关闭时传入 `schedule: null`。仅 Node/Docker 部署生效,serverless 平台返回 501。定时刷新计划持久化保存,失败后 10 分钟重试一次,服务重启后补执行一次。同步更新 README 文档、收藏列表返回 `scheduledRefreshSupported` 标志,并导出 `persistFavorites` 供调度模块调用。

* feat(redis): 新增收藏缓存实时刷新机制以解决多实例缓存不一致

在 serverless 多实例部署场景下,新增 getFavoriteCachesFromRedis 函数,在收藏相关请求时直接从 Redis 重新读取收藏数据并更新内存缓存,避免预热实例因首次加载空快照而错过其他实例新增的收藏内容。

* feat(favorite): 收藏刷新增加确认弹窗并明确缓存范围

- 收藏刷新操作前增加确认对话框,避免误操作
- 完善UI说明,明确只缓存剧集搜索结果,不缓存弹幕
- 为定时收藏按钮添加禁用态样式,提升交互一致性

* feat: 收藏增加最近刷新时间字段并支持按刷新时间排序

- 在收藏条目中新增 `lastRefreshAt` 字段,记录最近一次刷新时间;新增收藏时初始化为当前时间,刷新时更新为该次刷新时间。
- UI 中展示最近刷新时间,并调整收藏列表排序依据从收藏时间改为最近刷新时间。
- 更新 README 及 UI 说明文档,同步接口返回字段和界面显示描述。
- 兼容旧缓存,若无 `lastRefreshAt` 则回退为 `timestamp` 值。

* feat(ui): 将管理界面默认主题从 shinyo 改为 lavender

- 调整 UI_THEME 环境变量默认值为 lavender
- 将 lavender 置于主题列表首位,其余主题顺序保持不变
- 更新 resolveUiTheme 回退默认值及环境变量配置中的默认值
- README 文档同步更新默认主题说明

此变更使经典默认主题在首次访问时优先展示,提升用户体验一致性。
2026-08-08 22:16:56 +08:00
Elegy233 8ff78add1e feat(ui): 管理界面视觉重构,采用 Bangumi-syncer 柔和圆角设计风格 (#423)
* feat(ui): 管理界面视觉重构,采用 Bangumi-syncer 柔和圆角设计风格

整体参照 Bangumi-syncer 的前端设计系统,对 danmu_api 管理面板进行全量 UI 重构。

核心改动:
- 主题系统:8种强调色(shinyo新叶/sakura哔哩/tianyi天依/hatsune初音/sakuragi樱木/violet罗兰/amber LCL/lavender经典) + 独立明暗切换(data-color-scheme)
- 视觉语言:大圆角(16~24px)、多层浅阴影、胶囊形输入框(999px)、渐变按钮、cubic-bezier缓动
- Header 氛围:header弹幕彩色透明文字(词库~80条) + 背景弹幕全屏滚动 + 微光扫过(GPU加速)
- Header 玻璃模糊:nav-btn/badge 等低透明度元素叠加 backdrop-filter,弹幕穿过时可见模糊
- 明暗切换:右上角折角视觉 + 角标按钮,独立持久化
- 全 section 错峰 fade-up 入场,modal 毛玻璃遮罩(backdrop-filter) + enter/exit 动画
- prefers-reduced-motion 全量关闭动画
- 响应式:1200/992/768/480px 四断点,移动端 nav-btn 3 列 + content 14px 圆角容器
- 预览页:总览按钮常驻,点击顶级 Tab 返回总览,子分类选中态悬浮卡片效果
- 弹窗:scrollbar-gutter 稳定布局,弹窗内列表 bullet 与文字对齐
- slider:胶囊形轨道(::-webkit-slider-runnable-track) + 圆形 thumb
- 版本标识:最新版本可点击跳转 Telegram 更新通知频道
- 弹幕系统:DANMAKU_DICT 共享词库约80条(18位 GitHub 贡献者 @ID + 热门梗 + 公益提示),0.5~1.5s 发射(共享 DANMAKU_INTERVAL_MS),animationend 自动释放 DOM
- 底栏:项目声明 + 链接栏(GitHub/讨论组/频道/RSS/Forward 插件)
- README:特别感谢 Bangumi-syncer 提供的 UI 灵感
- 主题键 shinyo(新叶绿) 原为 sanae,已全量同步至前后端

性能:
- 所有动效使用 transform/opacity(GPU 加速),无 left/top 触发布局
- 弹幕元素生命周期:创建→动画结束/超时→remove,自动回收

已知问题:
- 暗浅色折角(.corner-fold)无法应用 backdrop-filter 模糊,受限于浏览器渲染管线(clip-path 在合成阶段未完全裁剪 backdrop-filter 采样区域)
- 界面主题(theme-settings)在 section 切换时无法触发通用入场动画,因该元素初始 hidden 使 CSS animation 错过触发时机

* fix(ui): 批量颜色输入框 textarea 使用直角圆角替代胶囊形

* fix(ui): 弹窗全局模糊右侧白条与内部滚动条刺破圆角

- html 添加 scrollbar-gutter:stable + background,弹窗隐藏滚轨时保留占位
  并填充遮罩同色 background,消除右侧白条
- html.modal-open 设置 overflow-y:hidden + 深色 background,隐藏滚轨
- ::-webkit-scrollbar 全局主题色渐变(10px),弹窗内单独覆盖为 6px 薄滚动条
  配合 margin:24px 0 避免刺破 20px border-radius
- 背景弹幕挂载点从 documentElement 移至 body,避免被 html overflow:hidden 裁剪

* fix(ui): 弹窗 gutter 背景色按暗浅色模式适配

浅色: rgba(0,0,0,0.58)  暗色: rgba(0,0,0,0.95)
通过 :has(body[data-color-scheme]) 跟随主题切换

* feat(ui): Dark Reader 防御适配与系统深浅色跟随

- 模板注入 darkreader-lock meta 阻断第三方暗色扩展注入
- :root 声明 color-scheme: light dark 告知浏览器原生双模式支持
- 首次无存储偏好时读取 prefers-color-scheme 跟随系统主题
- matchMedia change 事件监听系统主题实时变更(仅在用户未手动选择时生效)

* fix(ui): 合并规则关系按钮对齐与映射详情可读性

- 修复 merge-action select 与左右输入框底部对齐(hint 移入 label 内)
- 映射详情容器 padding-left 46→12px,行字号 12→13px
- 映射行新增斑马条纹与圆角
- .btn:hover 统一添加 translateY(-1px) 抬起效果

* fix(ui): 标签选项、暂存标签、合并模式切换、颜色池色块新增 hover 抬起效果

* fix(ui): 合并确认按钮 hover 闪烁 — Chrome 过渡合成循环

.confirm-merge-btn 28px 圆形按钮 margin-left:auto 在 flex 容器内
hover 时 transition:all 触发 background-color/box-shadow/transform
无限循环过渡。窄化为 box-shadow/filter/transform 三项显式声明
并加 !important 阻断 Chrome 自动注入合成属性。

* fix(ui): 审计修复 — 滚动条架构、gutter 色差、滑块统一、代码风格

- 删除 base.css.js 全局 ::-webkit-scrollbar(10px),保留 themes.css.js(8px)
- html 添加 color-scheme:light !important 修复 :root 声明导致 gutter 画布色偏差
- gutter rgba 0.58→0.59 微调匹配 blurred 背景色
- COLOR_POOL 亮度滑轨复用 .number-range 胶囊样式
- DANMAKU_INTERVAL_MS var→const 风格统一
2026-08-08 21:34:07 +08:00
Elegy233 c0788c43f8 fix(envs): resolveEpisodeTitleFilter吐槽添加反向否定,避免误杀正常剧集标题 (#422)
吐槽添加负向断言防止匹配以下正常标题:
- 本喵比起装傻更适合吐槽(前接能力/比较词)
- 吐槽艺人/吐槽役/吐槽大会/吐槽担当(后接角色/事件词)

同步更新.env.example和README.md中的对应正则。
2026-08-08 21:33:42 +08:00
Elegy233 72d848a1d7 fix(bangumi-data): 适配自定义源新版本号策略,更改版本号查询方式 (#421)
* fix(bangumi-data): 适配自定义版本号乘数格式

compareVersions新增patch归一化:patch>=1000时除以100还原上游
追踪版本号,与官方版本号逐段对比。适配upstreamPatch*100+internalRev
的自定义版本格式。版本查询失败时默认回退自定义源。

* perf(bangumi-data): 版本号查询从NPM Registry API改为CDN HEAD请求

fetchNpmLatestVersion替换为fetchCdnLatestVersion,利用jsDelivr
HEAD请求的x-jsd-version响应头提取模糊版号解析后的精确版本,
消除对registry.npmjs.org的依赖。HEAD仅传输报头无数据体。

selectBestDataSource新增版本查询并发锁(versionQueryPromise),
避免serverless冻结恢复后重复触发HEAD探测。
2026-08-08 21:33:20 +08:00
Elegy233 5e984a60a5 fix(dandan): 优化详情请求数,原名搜索添加低门槛预过滤避免出现完全无关结果 (#419)
* fix(dandan): 优化详情请求数,搜索返回前用低阈值titleMatches预过滤

search()返回前用titleMatches(t, keyword, season, false, 0.5)对原始搜索
结果做初筛,剔除明显无关条目避免浪费getEpisodes详情请求。0.5阈值仅挡
明显无关项,翻译差异结果放行交由handleAnimes含别名0.8最终过滤。

TMDB abort从原始搜索返回时推迟到预过滤后:保留结果则abort,初筛清空则
不abort以触发内部日语原名兜底。执行顺序由Promise.all改为先await原始再
决定是否await TMDB,确保abort在TMDB完成前生效。

* feat(dandan): 关联作品恢复预过滤误剔除的原始搜索结果条目

search()预过滤后在结果数组上记录原始animeIds,handleAnimes处理
关联作品时检查其animeId是否在原始搜索结果中被误过滤(如翻译差异
导致低阈值titleMatches未命中),自动补回至sourceAnimes与处理队列。

仅在未指定目标季时触发,避免干扰明确指定季度的场景。

* fix: 修复titleMatches相似度策略无法返回true的回归bug

commit 3345ed19添加年份兜底时将末尾return simMatch改为return false,
导致simMatch为true时跳过年份检查块后直接返回false。相似度匹配自7月14日
起在所有调用路径中静默失效,影响dandan预过滤及含别名0.8最终过滤。

预过滤titleMatches调用传入forceNonStrict=true,确保严格匹配模式下
仍使用包含+相似度双策略判断。

* perf(dandan): 日语原名搜索添加预过滤,常规预过滤阀门调整为0.4

常规搜索预过滤threshold由0.5调至0.4,更宽松以减少翻译差异导致的
误过滤。TMDB日语原名搜索结果新增threshold 0.19的低门阀预过滤,
剔除明显无关条目(如「正能量企鹅」等仅包含个别匹配字的噪声)。
2026-08-08 21:32:43 +08:00
Elegy233 463c14708a chore: 动画源dandan/bahamut/animeko排序置后,引导合理合并配置 (#418)
ALLOWED_PLATFORMS/ALLOWED_SOURCES/MERGE_ALLOWED_SOURCES
中将dandan/bahamut/animeko移至custom之前,同步更新
README.md和.env.example的对应排序。
2026-08-08 21:32:01 +08:00
Elegy233 8a284a6c8c fix(bilibili): Section API提取到代理检测外,无代理时也能触发回退 (#417)
pgc/web/season/section为直连接口不需要代理,从轨道二的
_hasBilibiliProxy()条件中提取至独立回退块。无代理配置时
Bangumi Data补充的港澳台条目也能通过直连Section接口获取
分集cid,正常获取弹幕。
2026-08-08 21:31:44 +08:00
huangxd- 5f17c51110 feat: 新增弹幕缓存最少条数配置,低于该值自动刷新 (#425)
* feat(cache): 新增弹幕缓存最少条数配置,低于该值自动刷新

- 增加 COMMENT_CACHE_MIN_COUNT 环境变量,默认100条,设为0可关闭
- 更新 README、.env.example 和 envs.js 配置定义
- 在缓存校验逻辑中,若缓存弹幕数低于阈值则忽略缓存时间重新获取
- 同步更新 UI 预览面板中的缓存相关配置项

* chore: bump version to 1.20.4

- 更新全局版本号从 1.20.3 至 1.20.4,以标记新迭代版本。

* refactor(ui): 重构环境变量编辑模态框的只读字段与状态管理

- 新增 `.readonly-field` 样式,用于在编辑模式下以虚线边框和灰色背景展示只读配置值
- 引入 `editingKeyName` 和 `editingType` 变量替代直接 DOM 读取,统一编辑状态管理
- 抽取 `getEnvTypeLabel` 工具函数,消除多处重复的类型标签逻辑
- 编辑时禁用分类、键名、类型等字段,避免误操作,同时保留文本可选择复制
- 关闭模态框时重置编辑状态,确保下次打开使用正确上下文
- 修改 `renderValueInput` 和 `updateTagStates` 等函数,使其依赖内存变量而非 DOM 元素,提升可维护性和可靠性
2026-08-08 21:31:12 +08:00
Elegy233 22ba26ae82 fix: 修复源不在优选平台时跨季被跳过,并使弹弹关联作品可参与跨季匹配 (#412)
handleAnimes发现关联作品后追加到sourceAnimes参数,不受canExpandRelateds
约束。同ID唯一性检查确保注入行为与搜索结果一致,不产生重复getEpisodes请求。
existingIds记录始终执行以消除并发竞态窗口。

跨季源过滤新增unsatisfiedPlatforms.size>0条件:当targetPlatform设了
但unsatisfiedPlatforms为空时不再跳过所有源。
2026-08-02 08:55:14 +08:00
huangxd- d0ce3b92d5 feat: 支持反向代理HTTPS协议配置,生成正确的对外链接 (#416)
- 新增环境变量 `DANMU_API_PUBLIC_PROTO` 用于指定对外协议
- 请求处理时优先读取 `X-Forwarded-Proto` 头,无则回退至配置变量
- 修复反向代理环境下弹幕链接协议错误的问题
2026-08-02 08:54:55 +08:00
huangxd- dcb4913ab7 refactor: 统一繁简转换处理,移除各数据源中的分散逻辑 (#414)
将原本在各个弹幕源(aiyifan、animeko、bahamut、bilibili、custom、dandan等)中根据 `globals.danmuSimplifiedTraditional` 条件调用 `simplized` 的代码全部移除,改为在统一出口处集中处理繁简转换。此举避免逻辑分散、减少重复代码,并便于后续维护和调整转换策略。
2026-08-02 08:54:17 +08:00
huangxd- a4b71d3dd6 refactor(ui): 重构配置预览和系统配置页,并可通过搜索框快速查找配置项 (#415)
* feat(ui): 重构配置预览界面并升级版本至1.20.3

- 新增预览工具栏与分类筛选按钮,支持按类别浏览环境变量
- 优化预览区域布局,采用粘性工具栏和响应式设计
- 调整 footer 样式,增加最大宽度并居中
- 重构前端预览逻辑,引入 previewState 管理分类数据与激活状态
- 同步更新版本号至 1.20.3

* fix(ui): 将预览页面的搜索输入框类型从"search"改为"text"

修复了预览页面中搜索输入框的输入类型,以避免因浏览器默认的search控件样式和清除按钮而造成的视觉不一致或误操作问题。

* feat: 为配置分类预览添加图标并移除概览标签

- 为各配置分类(API、源、匹配、弹幕、缓存、系统)的标签添加了对应的 Emoji 图标,提升界面可读性
- 移除导航中的“概览”分类,简化预览导航结构
- 调整目的:优化配置预览页面的视觉层次和用户体验

* feat(ui): 添加环境变量搜索功能并重构列表渲染逻辑

- 新增 envSearchQuery 状态和搜索输入框,支持按键名、值、描述、分类标签及主题名称过滤
- 提取 renderEnvNavigation、envItemMatchesSearch、renderEnvItem 等函数,优化渲染性能与可维护性
- 修复切换分类时搜索状态未清除的问题,并统一使用 clearEnvSearch 管理
- 关闭模态框时重置 editingKey 和 editingCategory,避免编辑状态残留
- 主题切换后自动刷新环境变量列表,确保 UI 主题标签即时更新
- 优化分类按钮高亮逻辑,移除对事件对象的依赖,改用 currentCategory 判断
2026-08-02 08:53:46 +08:00
Elegy233 ef003d7882 fix: 修复无法优选 qq 平台匹配 (#409)
PLATFORM_ORDER 优选平台匹配自 PR #167 起改为依据番剧身份(标题 from 段或 source)与目标平台评分。当源身份名与配置平台名不一致、但集上挂有目标平台标签时(如 tencent 源、集标题挂 qq),getPlatformMatchScore(identity, "qq") 得 0,与无交集的 dandan 同为 0 分,平局由 SOURCE_ORDER 决定,导致配置 qq 却选中 dandan 而非 tencent。

现于 matchAniAndEp 评分时,将命中集所挂平台标签并入候选平台(与身份标签去重合并),使 tencent 这类源在集上挂有 qq 时能正确加分。同时 getPlatformMatchScore 预处理支持全角&并去重,修正合并标题重复标签(如 dandan&qiyi&qiyi)抬高杂质长度导致评分失真的问题。该加性改动不影响既有的合并源优选评分(已以 40 例用例验证,含合并源、正常单源、全角&、去重及手动优选绝对优先等场景)。
2026-08-01 12:57:26 +08:00
Elegy233 e8f22b621f fix: 跨季扩展按平台满足度过滤源并修复恶性重复请求问题 (#408)
* fix: 跨季扩展按平台满足度过滤源并修复恶性重复请求问题

checkEpisodeSatisfied不再在首个不满足平台处break,改为遍历所有平台
通过新参数unsatisfiedOut记录不满足的平台集合。跨季扩展据此仅处理不满足
平台对应源,已满足者直接跳过不再被牵连重复调用handleAnimes。

跨季执行按源分组并发:源间Promise.all并行,同源内各季顺序执行确保
模块级缓存传播。源处理顺序遵循sourceOrderArr。

titleMatches新增threshold参数(默认0.8),maxSeason传入0.6放宽相似度
阈值使与查询翻译差异较大的结果仍能通过匹配参与季号提取。

* fix: 修复跨季场景下身份平台名与优选平台名不一致时的匹配失效

跨季分支 (PR #408) 的 checkEpisodeSatisfied 与 spillover 评分在判断某源是否为目标平台提供数据时,仅依据番剧身份(标题 from 段或 source),未并入命中集所挂的平台标签。当源身份名与配置平台名不一致、但集上挂有目标平台标签时(如 tencent 源、集标题挂 qq),该源被误判为「目标平台无数据」而跳过,导致配置 qq 却无法命中 tencent 源。

现于两处评分前,将候选平台由「身份标签」扩展为「身份标签与所挂集标签去重合并」(与 matchAniAndEp 的 1421 修复口径一致),使身份名(如 tencent)与优选平台名(如 qq)不一致、但集上挂有该标签的源也能被正确识别。该加性改动不影响既有的合并源优选评分(已以 21 例用例验证,含 tencent/qq 受影响案例与正常单源、身份命中、无平台、去重等不受影响案例)。

* fix: 修复未满足平台集合命名空间错配导致跨季扩展被跳过

问题:checkEpisodeSatisfied 在目标平台未满足时向 unsatisfiedOut 写入规范平台名(tPlat),而跨季扩展源过滤分支以源名(source)查询该集合,命名空间不一致使 PLATFORM_ORDER 模式下跨季扩展被静默整体跳过。

修复:在写入端就近收集提供该平台数据的 anime.source(providedSources),未满足时写入源名而非规范名,复用既有候选平台判定(actualPlatform),无新增映射表;仅当某 tPlat 子遍历确有数据且未满足时才写入对应源名,无关源不会被污染。

验证:新增 cross-season-unsatisfied-test.mjs 覆盖修复前后对比(tencent/qq、iqiyi/qiyi 受影响案例)、常规流程(首季命中/_any_/源名即规范名)与边界(多平台仅未满足源扩展、无关源不污染集合、主返回值契约不变)共 36 项全通过;既有跨季平台匹配测试 21/21 不受影响。
2026-08-01 12:56:58 +08:00
haode f19a5d9c4c feat: bangumi、episode 接口返回数据新增 url 字段 (#403)
* Add files via upload

* Add files via upload
2026-08-01 12:55:27 +08:00
huangxd- 13e88bec25 优化移动端系统配置操作按钮布局,红果源支持提取并显示短剧年份信息 (#411)
* feat(forward): 优化缓存管理,支持版本控制和分段缓存

- 引入缓存schema版本管理,避免旧版本缓存数据污染
- 添加分段缓存前缀、TTL及空重试最小间隔,提升缓存灵活性
- 使用内存缓存(Map)减少重复存储读取
- 重构getCaches逻辑,增加加载状态标识避免重复初始化
- 抽取parseForwardCacheValue统一解析校验,增强健壮性
- 缓存版本变更时自动清理旧数据,确保数据一致性

* style: 更新lavender主题配色并调整UI主题选项顺序

更新lavender主题样式,采用渐变背景和全新配色方案(蓝紫到粉紫渐变),使界面更鲜明;同时调整UI_THEME环境变量及内部主题列表中lavender的顺序,确保各配置保持一致。

* fix(ui): 优化移动端系统配置操作按钮布局

* feat(hongguo): 支持提取并显示短剧年份信息

- 新增 extractYearFromTimestamp 工具函数,从多种时间戳字段中解析年份
- 在搜索结果和剧集详情中集成年份提取,填充 anime 对象的 year 字段
- 修改 animeTitle 格式,在短剧名称后追加 (年份) 以提升识别度
- 补充 album_info 等备用数据源,增强解析健壮性
- 修复多视频详情接口响应结构兼容性问题,避免数据丢失
2026-08-01 12:55:06 +08:00
Elegy233 130e9886f3 fix(iqiyi): 优化分季URL请求数,分集异常时自动重试 (#405)
* fix(iqiyi): 分季URL按目标季过滤、跨调用及并发去重并统一标签为小写

旧版 album_episodes 分支对每个分季组的 videos URL 无差别地发起请求并合并到同一结果,产生三类问题:
1. 指定季时仍拉取并合并所有无关季分集(无季过滤);
2. 同一分季在同一次乃至跨多次 getEpisodes 调用中被重复请求(无去重);
3. 跨多次调用并发场景下,多个调用在缓存写入前同时发起同一分季请求(缓存击穿)。

修复方式:
- getEpisodes 新增 querySeason 与 seasonAlbumCache 参数;
- handleAnimes 透传解析出的目标季,并为整个搜索批次创建共享的 album_id 缓存;
- album_episodes 分支按 group.tab_name 用 getExplicitSeasonNumber 解析季号,指定季时跳过非目标季组;
- 以 album_id 为键缓存分季请求本身(而非仅结果):首次请求时立即记录 Promise,并发或后续调用复用同一请求,确保同一分季在一次搜索内最多请求一次;
- 日志标签 [iQiyi] 全量改为小写 [iqiyi],与其他源规范保持一致;
- video_list 内联分支不受影响,未指定季时行为与之前一致(仅减少冗余请求)。

测试 iqiyi.season.test.mjs 覆盖季过滤、单次/跨次/并发去重、内联分支等场景,共 8 例全过。

* fix(iqiyi): getEpisodes 的 base_info 请求失败时限时重试两次

问题:base_info 接口偶发返回空响应或结构残缺(status_code 为 0 但缺失
data.template,典型网络抖动)。原逻辑在 data.status_code !== 0 || !data.data ||
!data.data.template 时直接 return [],而 handleAnimes 仅在分集非空时才收录该
结果,导致一次瞬断会令整个季被静默丢弃,需重跑才能恢复(见老友记4.txt:
一次运行缺第七季,重跑即正常)。

修复:参照本文件搜索接口已有的「延迟重试两次」先例,将 base_info 的 httpGet
与解析封装为 fetchEpisodeData,对空响应、JSON 解析失败、status_code 非 0 或
结构残缺的情况最多重试两次(每次间隔 3 秒);耗尽仍未拿到合法响应才 return []。
首请求即成功时循环条件不满足,行为与之前完全一致。

* 优化(iqiyi): 全季搜索跳过已被内联覆盖的季的分季URL请求

全季搜索(不指定季)时,handleAnimes 先统一拉取各结果的 base_info,收集 album_episodes 中以内联 feature_paged 形式存在的季 entity_id 集合(inlinedAlbumIds)并缓存复用,避免重复请求 base_info;处理各结果时,若某分季组是 URL 桩且其 entity_id 已在内联集合中,跳过其 selector 请求。指定季时预拉取阶段不执行,桩季照常请求以保证指定季一定返回。覆盖判定以季 entity_id 为键,不会误吞衍生内容或错删无关命中数据。
2026-07-29 22:39:13 +08:00
Elegy233 436dd63f15 perf(ui): 优化日志查看器在大量日志下的渲染与筛选性能 (#404)
- 日志行加 content-visibility:auto,视口外日志跳过布局与绘制,
  全量渲染 9000 行时布局成本从数百毫秒降到个位数毫秒
- 分类筛选由整段 innerHTML 销毁重建改为切换 CSS class 显隐,
  不再重建日志 DOM
- addLog 仅在日志视图可见时重建 DOM,避免非日志页面产生无谓的全量重建
- 切到日志视图仍每次从服务端加载最新日志,保留原有及时性逻辑

不影响 GET /api/logs 服务端接口;不限制渲染行数,日志完整保留
main.js 保持上游原版未改动
2026-07-29 22:38:48 +08:00
Elegy233 825ddc25bb fix: 修复严格匹配下记住手动选择结果功能失效及匹配遍历过早终止 (#401)
* fix: matchAniAndEp 移除对非优先条目的独占过滤,保留评分优先

原代码通过 preferAnimeId 直接 continue 跳过所有不匹配的优先条目,
其意图是确保优先条目被命中。但该过滤存在两个问题:

1. 与 isPreferredAnime 的 +9999 评分加成功能重复——评分已提供
   压倒性优先权,独占过滤属于冗余保护。
2. 独占过滤阻止了优先源缺失目标集时的自然回退:其他源被
   跳过导致无匹配,需要额外走 fallbackMatchAniAndEp 兜底。

移除独占过滤后,isPreferredAnime 的 +9999 评分加成和匹配后的
break 仍然确保优先条目在正常匹配中绝对优先,同时允许优先源
无目标集时自然回退到其他可用源。

* fix: TITLE_NOISE_FILTER 默认规则增加方括号杂音词支持

默认 TITLE_NOISE_FILTER 仅匹配圆括号内的杂音词(如(日配)、(国配)),
遗漏方括号形式的变种(如[日语版]、[国语版]、[中字])。
tencent 源的标题格式"假面骑士ZZZ[日语版](2025)"中[日语版]未被清理,
导致 titleMatches 在严格匹配下的标题清理不完整。

修复:默认规则扩展为单字符类同时匹配圆括号(中英文)和方括号,
在原有词表基础上新增方括号特供词:日语版、国语版、英语版、中字。
圆括号兼容保持不变。

* fix: 偏好记录流程的titleMatches强制使用非严格匹配,不因STRICT_TITLE_MATCH受影响

titleMatches 在全局 STRICT_TITLE_MATCH 开启时统一走 strictTitleMatch,
但偏好记录(setPreferByAnimeId)场景应始终使用非严格匹配。原因:

1. 用户手动选择某个源的弹幕即表示"我认可这是正确匹配",
   不应因标题含[日语版]等形式差异而被拒。
2. strictTitleMatch 限制 suffix 只能为 seasonPattern 中的模式,
   而(2025)、[日语版]等源补充标识均不符合,导致有意义的用户选择被丢弃。
3. "记住手动选择结果"强调自由选择权,严格匹配的搜索过滤逻辑不适用于此。

修复:titleMatches 增加 forceNonStrict 参数(默认false,完全向后兼容),
偏好记录流程传入 true,跳过 strictTitleMatch 直接走包含匹配(includes)。

* fix: matchAniAndEp 无平台偏好时 break 应感知 preferAnimeId,防止跳过优先条目

第1435行 if(!platform || isPreferredAnime) break 在 platform 为空时
无条件跳出循环,导致即使 preferAnimeId 指向靠后的条目,
循环也会在第一个匹配条目后终止,优先条目从未被评估。

修复:break 仅在两种场景触发:
1. isPreferredAnime=true — 已命中优先条目,无需继续
2. !platform && !preferAnimeId — 无平台偏好且无优先条目,首个匹配即可
当 preferAnimeId 存在但尚未命中时,继续循环评估后续条目。
2026-07-29 22:37:17 +08:00
Elegy233 c9112ee7cf fix(tencent): 支持单 cid 综艺的多季分集获取 (#399)
综艺单 cid 多季内容收在 videoInfo.episodeSites/playSites 的 chapterInfo 下,
tabs 为空时仅取默认季分集,分季内容不完整。

- 按分页 tabs 下发的 page_context 结构重建各季请求参数:chapterInfo 自带的
  pageContext 缺少 cid/page_num/req_type 等必填项,服务端忽略并回落默认季;
  复用 tabs 路径的字段结构以切到对应季。
- 子分类式章节仅取“正片”子分类(标题为“正片”,作为 chapter_name 发往分页接口),
  与 tabs 路径以 chapter_name=正片 标识正片的方式一致,避免引入花絮/直播回放/直拍。
- 按响应 module_params.has_next(字符串或布尔)逐页翻页取全分集;章节内容不按标题过滤,
  仅排除 is_trailer=1 的预告片,加更/彩蛋/抢鲜看等章内内容与主集一并保留。
- 多季按标题年份与春夏秋冬播出季序排序后合并,跨季按 vid 去重,单季内按 episode_on_chapter 排序。
2026-07-29 22:36:51 +08:00
Elegy233 9d31e85dd1 fix: 修复 Bangumi-Data 检索子串混淆与尾部数字误识别为季号,使高达00正确匹配而非高达0079 (#398)
* fix: getTmdbJaOriginalTitle 对 Bangumi-Data 结果按精确度排序,防止子串误匹配

searchBangumiData 按数据文件原始顺序返回匹配结果,未按精确度排序。
当检索词为'机动战士高达00'时,Bangumi-Data 中 '机动战士高达0079'(1979) 和
'机动战士高达00'(2007) 均匹配,但因 0079 在文件中排在前,localMatches[0]
取到 1979 条目,日语原名错误提取为 '機動戦士ガンダム'(非00)。

后果:
- dandan 使用错误的日语原名搜索 episodes 接口 → 匹配到 0079 条目
- bahamut 使用错误的日语原名搜索 → 大量 0079 结果污染匹配列表

修复:取首个结果前按精确匹配排序,将正好与检索词相同标题的条目排在最前。
无精确匹配时维持原始顺序,确保对其他检索词无副作用。

* fix: extractSeasonNumberFromAnimeTitle 对尾部"00"等非法季号返回 null,防止误过滤

extractSeasonNumberFromAnimeTitle 规则4(尾部阿拉伯数字)对如"机动战士高达00"
匹配到"达00"中的"00",parseInt("00")=0 → 返回 season=0。
导致在 handleAnimes 的季号过滤中 s=0≠1 且 s≠null,条目被误过滤。

修复:尾部数字解析为0时返回 {season:null},因为动画领域不存在"第0季"。
2026-07-29 22:36:23 +08:00
Elegy233 776531128a fix: 修复跨季集数匹配中多候选毒化、同号集膨胀、季容量虚高与无关源污染 (#397)
* fix: findCrossSeasonEpisodeMap seasonMap 被剧场版/电影条目毒化导致跨季映射实效

当同一季号存在多个候选条目时,!seasonMap.has(sNum) 以先到先得方式保存,
导致集数极少的剧场版(1集)占据 TV 系列应有的季号位置。
如咒术回战 searchData 中死灭回游/怀玉玉折(sNum=1,1集)排在前面,
真正的 S1 TV系列(24集)被跳过,seasonMap S1仅1集使跨季映射崩溃。

修复:比较集数,保留集数更多的候选条目。TV 系列自然覆盖剧场版。

* fix: filterSameEpisodeTitle 增加按集号去重,修复360聚合源多平台同名集膨胀

360 等聚合采集源将多平台同一集合并到单个条目中(如 imgo/youku/qiyi/bilibili 各 24 集
→ 96 links),filterSameEpisodeTitle 仅按标题去重(不同平台前缀不同故不过滤),
导致 checkEpisodeSatisfied 误判集数满足跳过跨季扩展、findEpisodeByNumber 索引映射到错误集、
seasonMap 容量虚高使跨季映射失效。

修复:同名去重后增加按 extractEpisodeNumberFromTitle 的二次去重,
同一集号保留首次出现的平台条目。无合法集号(null)的标题不受影响。

* fix: checkEpisodeSatisfied 跨季容量求和不应包含超过查询季号的后继季

seasonCapacities 的逐季容量在求和判断时原本无条件累加所有季号,
导致别名含"Season 3"的死灭回游(18363)被映射为sNum=3并累计12集后
与S1的24集合计36 ≥ 26,使跨季扩展被抑制。

修复:仅累加 sNum ≤ querySeason 的容量。超过查询季号的后继季
(如S3)在S2缺失时不应独立计入总量,因其实际依赖前序季的解析。

* fix: 跨季扩展 maxSeason 仅从与查询相关的条目提取季号,防止无关结果污染

maxSeason 遍历所有源的原始搜索结果,使用 a.animeTitle || a.title 提取季号。
animeko 结果字段为 name/name_cn(不含 animeTitle/title),单独用时提取不到
季号。但当 renren 同时启用时,其搜索返回的无关条目(标题含高季号但不匹配查询)
被 a.title 捕获,异常推高 maxSeason 触发无效跨季扩展。

修复:字段覆盖 a.animeTitle || a.title || a.name || a.name_cn,使用
titleMatches(testTitle, queryTitle) 过滤,仅从相关条目中提取季号。

* fix: maxSeason 的 titleMatches 添加 forceNonStrict=true,兼容 STRICT_TITLE_MATCH

titleMatches 在全局 STRICT_TITLE_MATCH 开启时统一走 strictTitleMatch。
strictTitleMatch 限制标题后缀仅能为 seasonPattern 中的模式,而源补充的
"(续篇)"、"年番"等标识不符合,导致相关的高季条目被错误过滤。
forceNonStrict=true 跳过严格匹配直接走包含匹配,确保 maxSeason 的宽匹配
语义不被全局严格模式干扰。该参数尚未合并(PR #401),JS 多传实参无副作用。

* fix: maxSeason 不足时仍尝试一次跨季扩展,兼顾关联作品发现机制

某些源(如 dandan)的高季条目经 handleAnimes 内的关联作品发现机制获取,
原始搜索结果中无直接季号标记。当 maxSeason(从原始搜索结果提取的季号)
不大于 querySeason 时,仍尝试至少一次跨季扩展(S+1),确保跨季执行中
触发源的关联作品发现逻辑,避免因原始搜索无高季标记而跳过有效季。

同时修复 maxSeason 的 titleMatches 过滤:字段覆盖 title/name/name_cn,
使用 titleMatches 过滤与查询无关的条目,避免无关源搜索结果的季号污染。

* Revert "fix: maxSeason 不足时仍尝试一次跨季扩展,兼顾关联作品发现机制"

This reverts commit 363ad56c62adfe5fedd229343f4447fc4cd2bad6.
2026-07-29 22:36:00 +08:00
huangxd- 370930ddb2 feat: 扩展AES支持多密钥长度并重构migu-util,红果支持多API主机故障转移与响应解析优化 (#406)
* feat: 扩展AES支持多密钥长度并重构migu-util

- codec-util: keyExpansion支持16/24/32字节密钥,解密函数动态计算轮数,支持AES-128/192/256
- codec-util: 重命名aesDecryptECB为aesEcbDecrypt并增加输入校验,提升健壮性
- migu-util: 移除大量WASM相关代码及依赖,简化解密逻辑,降低维护成本
- 统一使用标准AES ECB/CBC模式,替代原有WASM网关解密方案

* refactor(aes): 拆分密钥扩展与解密,为红果 X-Argus 固定 AES-128 路径

- 将原通用的 keyExpansion 和 aesDecryptBlock 明确为 AES-128 专用实现,并新增 keyExpansionForKeySize 和 aesDecryptBlockForKeySize 以支持 192/256 位密钥
- aesEcbDecrypt 现根据密钥长度选择对应实现,保持对外接口不变
- 原因:红果 X-Argus 依赖固定 AES-128 调度路径,拆分后便于维护且避免误用其他密钥长度影响该业务逻辑

* chore: 升级版本至1.20.1并优化密钥扩展注释

- 将全局版本号从1.20.0递增至1.20.1
- 更新`keyExpansion`函数的注释,明确其功能为将16字节密钥扩展为176字节,移除原有冗余描述
- 版本升级旨在反映当前代码状态,注释调整提升代码可读性

* feat(hongguo): 支持多API主机故障转移与响应解析优化

- 将固定 apiHost 扩展为 apiHosts 列表,并引入首选主机与轮换机制,提升请求容错性
- 新增 getApiHosts 方法按优先级返回可用主机,request 方法在主机失败时自动切换重试
- 提取 parseApiResponse 统一处理响应校验与 JSON 解析,增强错误信息可读性
- 移除 MAX_RETRIES 常量,改用主机列表轮询替代简单重试,并标记不可重试的业务错误
- 优化 comment 请求的并发控制与响应检查逻辑,减少无效请求

* fix: 修复模态框打开时页面背景滚动问题

- 新增 CSS 样式,当 modal 打开时锁定 html 和 body 的滚动
- 添加 lockPageScroll / unlockPageScroll 函数,记录滚动位置并在关闭后恢复
- 在编辑环境时调用锁定,关闭模态框时解锁,提升用户体验
2026-07-29 22:35:35 +08:00
huangxd- 84d22da9a9 docs: 更新项目结构文档,新增若干文件及目录 2026-07-27 08:03:05 +08:00
huangxd- 0e93064321 fix: 修复环境变量列表中过滤 UI_THEME 后编辑删除索引错乱的问题
在渲染环境变量列表时,过滤掉 UI_THEME 项后,原索引与显示位置不匹配,导致编辑和删除操作错误。现保留原始索引,确保操作指向正确的配置项。
2026-07-26 22:01:25 +08:00
huangxd- 22ba6dabd0 feat: 将 dan-any 模块提取为独立工具并优化构建流程
- 在 build-forward-widget 中为 dan-any.js 添加解析和加载钩子,将其排除出 widget 捆绑包,仅保留转换功能占位
- 新增 convertDanAny 函数封装所有 dan-any 格式转换逻辑,支持 JSON/XML/Binary 输出
- 重构 danmu-util 中的格式响应函数,移除直接依赖 @dan-uni/dan-any,改为调用本地 convertDanAny
- 清理冗余导入,降低模块耦合,为后续 forward 独立部署做准备
2026-07-26 21:53:19 +08:00
huangxd- 089530f7ed chore: 移除 package.json 中的 devEngines 配置
不再强制指定 pnpm 版本,以简化项目管理并避免版本锁定带来的不必要约束。
2026-07-26 21:08:34 +08:00
Elegy233 d4d4c9fffa fix: 完善跨季匹配与平台过滤逻辑,修复备用匹配状态及集标题解析 (#392)
* fix: 跨季扩展阻塞与备用匹配 isMatched 状态修复

checkEpisodeSatisfied 指定平台无匹配数据时返回 false:
指定目标平台(如 qiyi)但搜索结果均不匹配该平台时,不再误判为满足。
新增 anyPlatformHadData 标记,无匹配平台时返回 false 以触发跨季扩展。

备用匹配成功后设置 isMatched:
fallbackMatchAniAndEp 成功匹配剧集后 isMatched 未正确置为 true,
导致客户端收到匹配结果但标记为未匹配。

* fix: checkEpisodeSatisfied 优选平台无匹配时回退到不区分平台重查而非直接返回false

当 PLATFORM_ORDER 指定的平台与搜索结果源不匹配时(例如 PLATFORM_ORDER=qiyi 但搜索结果来自 renren),
anyPlatformHadData 为 false,原先直接返回 false 会错误触发跨季扩展(S5~S10)和 spillover 匹配,
造成大量无谓 API 请求。

修复:anyPlatformHadData 为 false 且有 targetPlatform 时,以 targetPlatform=null 递归调用自身,
回退到 _any_ 模式重新查验全部可用数据,再依据集数是否满足决定是否触发扩展。
这同时正确覆盖两种场景:
- 当前季集数不足(如 S1E31 仅 11 集)→ 正确触发扩展
- 当前季集数充足但平台不匹配(如 S4E3 在 renren 有 24 集且含 E3)→ 正确抑制扩展

* fix: matchAniAndEp与findCrossSeasonEpisodeMap中平台不匹配源时回退到无平台过滤

当PLATFORM_ORDER与搜索结果源不匹配时(例如PLATFORM_ORDER=renren但搜到iqiyi结果):
1. matchAniAndEp: findEpisodeByNumber因平台过滤丢弃所有集数返回null后,
   新增回退:检查候选源是否匹配指定平台,不匹配则以platform=null重试。
   这避免了bestRes为空而错误触发findCrossSeasonEpisodeMap spillover。
2. findCrossSeasonEpisodeMap: 平台不匹配时以currentSeason++ + continue
   替代break,允许跨季映射在后续匹配季中继续查找而非直接退出。

保护措施:isPreferredAnime的原有回退逻辑保留作为最后一层兜底。

* fix: extractEpisodeNumberFromTitle 支持第X话和第X回格式

此前仅识别 /第(\d+)集/,导致 dandan 等源使用第31话格式的集标题
无法被提取集号,进而 findEpisodeByNumber 对所有分季 dandan 条目返回 null。
跨季扩展虽然正确拉取了 S3 dandan 条目的 E31(第31话 决裂),但
extractEpisodeNumberFromTitle 无法提取数字 31,导致匹配被跳过,
最终落在能识别第31集的 bahamut 不分季条目上。

修复将正则扩展为 /第(\d+)[集话回]/,同时覆盖:
- 集:标准中文格式(如第31集)
- 话:日式动画格式(如第31话)
- 回:旧式分回格式(如第12回)
不匹配 期/章/卷,避免错配综艺/漫画/OVA分卷格式。

* fix: titleMatches 对无季号标题放宽季数守卫,避免不分季长剧被全部拦截

titleMatches 在 querySeason>1 时原先对无季号标题作 (null||1)!=querySeason
拦截,导致 iqiyi 搜索航海王 S2E62 时全部 16 个结果因标题中无季号被过滤,
searchData 为空,匹配链路崩溃。

修复:仅当源标题明确含季号且与目标季不一致时才拦截,无季号的不分季长剧
交由上游源处理器(如 iqiyi 季数预过滤)决定是否保留。

影响分析:
- 不分季长剧(querySeason>1):修复前被拦截 → 修复后通过 → 正确
- 分季剧S1标题(querySeason=2):修复前拦截 → 修复后仍拦截 → 不变
- 分季剧S2标题(querySeason=2):修复前通过 → 修复后仍通过 → 不变
- 分季剧S3标题(querySeason=2):修复前拦截 → 修复后仍拦截 → 不变
- querySeason=1或null:不受此分支影响,原有行为完全保持
2026-07-26 21:04:12 +08:00
Elegy233 f4d6e5781b feat: 合并工具支持标题年份一致时豁免类型标签差异 (#391)
标题与年份完全一致时跳过媒体类型(电视剧/动漫)冲突检测。
RegexStore.Clean 新增 YEAR_DIGITS,用于匹配捕获四位数年份。
年份不同、N/A 或无年份不触发豁免。
2026-07-26 21:03:56 +08:00
Elegy233andhuangxd- 2b34bae417 feat: renren 全阶段超时调整与云环境两两接口并行,搜索连通性优化详情 (#390)
超时调整:
- 搜索接口超时 3000ms
- 详情接口超时 3000ms
- 弹幕接口超时 5000ms
- renrenRequest 与 renrenHttpGet 新增可选 timeout、signal 参数

云环境两两并行 + 首成即止(搜索和详情):
- 搜索:WIN 和 TV 并行,MAC 和 WEB 并行。TV 优先于 WIN,MAC 优先于 WEB。
- 详情:WEB 和 TV 并行,MAC 和 WIN 并行。WEB 优先于 TV,MAC 优先于 WIN。
- 主级成功时通过 AbortController 中止次级请求,无数据不触发中止。
- 本地环境保持串行降级逻辑不变。

搜索连通性优化详情:
- 搜索阶段记录各 tier 连通性(ok / timeout / unknown)。
- 详情阶段跳过搜索中已超时的 tier,优先使用已验证工作的 tier。
- 连通性仅在单次搜索到详情的链路内有效,每次搜索入口清零。
- 主级被跳过时,次级工作则采纳,失败时回退主级。

详情优先级调整为 WEB,TV,MAC,WIN。

Co-authored-by: huangxd- <649924757@qq.com>
2026-07-26 21:03:40 +08:00
Elegy233 a4256c2885 fix: 严格匹配支持剧名杂音清理,改用正则补全数字与英中季号允许关键词 (#389)
strictTitleMatch 入口处先执行 titleNoiseFilter 再 normalizeSpaces,
标题与查询词双端清理后比较,与宽松模式行为一致。
移除 normalizeSpaces 已剥离的无用符号分隔词(空格、括号、冒号等)。

允许关键词检测改用正则 seasonPattern,覆盖阿拉伯数字(新闻女王2)、
罗马数字(无职转生Ⅱ)、英文季号(S1/Season1/Part1)、
中文季号(第一季/第三部)及关键词(年番/合集/部/篇/部分/剧场/完结/最终)。
裸 S/第/Season 无数字后缀不再匹配,部(?!分) 防止裸"部"吞掉"部分"。
2026-07-26 21:00:58 +08:00
Elegy233 4c3321ee51 fix: 同平台的不同链接弹幕请求串行延迟 1 秒,防止风控 (#388)
当同平台存在多个不同链接时(链接合并的多 URL、合并源
的多配音版本等),改为串行执行,每个之间间隔 1 秒,
防止短时间内对同一平台发起多次并发请求触发风控。
不同平台仍保持并行执行,互不影响。

仅影响 fetchMergedComments 中的多链接分支,
常规单源流程不受任何影响。
2026-07-26 21:00:45 +08:00
Elegy233 ad60e299bd fix: iqiyi 搜索风控重试间隔从 1.5 秒延长至 3 秒,并优化年份提取 (#387)
* fix: 爱奇艺搜索风控重试间隔从 1.5 秒延长至 3 秒

用户反馈遇到风控时当前 1.5 秒间隔不足以通过,等待一会后又可正常访问。
推测爱奇艺风控冷却窗口约 3-5 秒,延长至 3 秒以覆盖更多场景。

* fix: 爱奇艺搜索意图聚合卡片支持从 superscript 角标获取年份

搜索接口 homePageV3 返回的意图聚合卡片(template 112)不含 year 字段,
年份实际存放在角标字段 superscript(如 "1999"),导致航海王等内容年份恒为 null。
在原有 year.value || year.name 回退链末尾追加 superscript 兜底,使其在普通结果卡片
与意图聚合卡片下均能正确提取 4 位年份。
2026-07-26 21:00:20 +08:00
Elegy233 a18a7b5174 fix: 合并合集探测分离 3D/2D 维度,防止误判断合并 (#386)
getContentCategory 对 2D 动漫和 3D 动漫均返回 ANIME,
导致同 baseTitle 的 3D 番进入与 2D 番同一合集探测组。
如 tencent 择天记(2020)【动漫】(2D,61集) 与 iqiyi
择天记(2026)【3D动漫】(3D,26集) 被误分到同一组而产生错误合并。

合集探测分组 key 中加入维度标记(3D/2D/-),使 3D 与 2D
番剧互不进入同一探测组,合集探测各查各的。
2026-07-26 21:00:07 +08:00
Elegy233 d3d15567a1 fix: 修复集连播场景可能导致集偏移记录错误 (#383)
* fix: 集偏移记录加 3 分钟 TTL 防止 lastSearch.episode 过期

lastSearch 仅在 Match/Search 时更新。用户顺播下一集时不刷新,
导致 Comment 记录错误偏移(如 E21 时搜索,20 分钟后顺播 E24
仍以 E21 为基准记录 "21:第24集")。

加 3 分钟时间窗口:Match 后 3 分钟内的 Comment 才记录偏移。
- 手动纠正:Match 后立即纠正,3 分钟内生效
- 顺播过期:超过 3 分钟自动跳过,不产生错误偏移
3 分钟与默认搜索结果缓存时间一致。

* Refine comments on lastSearch handling and TTL

Updated comments for clarity regarding lastSearch behavior and TTL handling.
2026-07-26 20:59:51 +08:00
Rinne 57e693f3d5 feat: 支持更多类型的弹幕输出 (#382)
* feat: 添加基于 @dan-uni/dan-any 的多弹幕格式输出支持,并优化弹幕数据格式化响应

* feat: 更新 README 关于支持多种弹幕格式输出的内容,并添加环境变量配置选项
2026-07-26 20:59:36 +08:00
Elegy233andhuangxd- a8147a9811 fix: 日志标签分层、UI渲染性能优化、全项目标签统一小写 (#380)
标签分层: [Utils] 全项目归入 [system],减少不必要分类层级
性能优化: getLogCategory 预计算为 _category 字段,
  renderLogs/updateFilterUI 直接读取,5k行场景正则在10000+→0次
裸标签补全: [Path Check]/[Path Fix] → [system] [Path ...];
  Bangumi-Data 直调 fetch 的 [请求模拟] 补 [system] 前缀
标签统一: 源文件 log() 中的大写标签([Server]/[Animeko]/
  [Bilibili]/[Bilibili-Proxy]/[Tencent] 等40+种)统一为小写

Co-authored-by: huangxd- <649924757@qq.com>
2026-07-26 20:59:19 +08:00
huangxd- 3a6b5340a0 改用opencc-js处理繁简转换 (#395)
* refactor: 优化构建脚本并改用opencc-js处理繁简转换

- 重构 `build-forward-widget.js` 中的行删除逻辑,改用 split/filter 按片段匹配,避免单行大字典导致正则回溯或内存问题
- 增加 `esbuild.stop()` 确保构建进程正常退出,将 `process.exit(1)` 改为 `process.exitCode = 1` 以允许清理
- 将 `zh-util.js` 中硬编码的繁简映射替换为 `opencc-js` 库,大幅精简代码并提高转换准确性和可维护性

* refactor(utils): 更新 opencc-js 转换器初始化方式

改用 ConverterFactory 并显式导入字典文件,避免使用已弃用的 Converter 构造函数,确保与最新 opencc-js API 兼容。
2026-07-26 20:56:45 +08:00
huangxd- c4d2951eb5 新增红果短剧源 (#394)
* feat: 新增红果短剧源支持

- 在 README 和配置示例中添加红果短剧(hongguo)源,扩展弹幕获取来源
- 更新 SOURCE_ORDER、PLATFORM_ORDER、MERGE_SOURCE_PAIRS 等配置项的可选值
- 新增 hongguo.js 源文件,完善项目文档和目录结构说明

* fix: 支持通过红果播放链接获取弹幕

* feat: 添加红果短剧全集合并选项,支持按集号顺序合并所有集弹幕

- 新增环境变量 `HONGGUO_MERGE_ALL_EPISODES`,默认为 false,启用后将红果短剧所有集的弹幕按剧集顺序合并为一集返回,每集弹幕时间累加前面各集时长,剧集列表显示为“全集”
- 修改缓存键生成逻辑,当启用合并时在缓存键后追加标识,避免与未合并缓存冲突
- 新增 `parseHongguoSeriesId`、`isHongguoSeriesId` 等工具函数用于识别红果系列 ID
- 更新 README 和 `.env.example` 文档说明新配置项

* test: 增强 forward-widget 测试流程的健壮性与剧集匹配逻辑

- 注释掉 storage 模拟中的调试日志,减少测试输出干扰
- 增加进程退出码设置,测试失败时正确返回非零状态
- 新增搜索结果为空时的异常抛出,避免后续空引用
- 优化剧集选择:优先按集数精确匹配,回退到数组索引,增加匹配失败提示
- 新增弹幕分片选择:优先选择包含 450 秒左右的分片,否则取中间时间点
- 增加对分片列表和弹幕列表为空时的异常检查,确保链路完整性
- 调整输出日志,明确显示所选剧集、分片和请求时间点,便于调试

* test: 更新 forward-widget 测试用例参数

- 调整测试数据中的 episode、sourceOrder 和 title 值
- 新增 hongguoMergeAllEpisodes 配置项用于测试
- 更新测试场景以覆盖新逻辑

* feat(forward): 支持debug构建并添加真机远程日志回传

- 新增 `--debug` 构建参数,生成 `logvar-danmu.debug.js` 供真机调试使用
- debug 版本将 `httpGet/httpPost` 替换为带日志回传的调试实现
- 实时回传 handler 执行、请求详情、耗时、异常及 console 输出到服务端终端和 `/api/logs`
- 敏感信息(Cookie、token、API key)在回传前自动脱敏,且回传失败不影响主流程
- 更新 README,补充 Forward 真机调试步骤和使用说明
2026-07-26 20:56:32 +08:00
huangxd- 1bb163e77d feat: 添加环境变量配置导入导出功能以及管理界面主题配置UI_THEME (#393)
* feat: 添加环境变量配置导入导出功能

- 新增 exportSystemConfig 函数,支持将当前环境变量配置导出为 JSON 文件
- 新增 import 相关函数,支持选择并导入配置文件,自动校验键名格式与值类型
- 导入时按优先级排序(TOKEN、ADMIN_TOKEN 等),避免权限提前失效
- 处理脱敏值(全星号)跳过导入并给出明确提示

* feat: 添加管理界面主题配置UI_THEME

增加UI_THEME环境变量,支持ocean、forest、graphite、berry、monochrome、sunset、aurora、lavender、mist、terminal共10种主题,默认值为ocean。在系统API中解析并应用该配置,使管理界面可根据用户偏好切换视觉风格,提升使用体验。

* 更换导入导出配置的图标

* chore: bump version to 1.20.0

* feat(ui): 支持浏览器本地保存主题偏好,优化主题切换错误处理

- 新增 localStorage 存储用户主题选择,优先于环境变量 UI_THEME 与部署默认值
- 主题切换时同时保存本地偏好,云端保存失败时降级为本地生效并给出友好提示
- 更新 README 中 UI_THEME 说明,明确浏览器本地主题优先于环境变量
- 重构主题相关函数,提升可维护性
2026-07-26 20:56:09 +08:00
huangxd- ce8b3cbddf feat: 为ForwardWidget构建添加运行时兼容插件,并重构解压工具函数 (#376)
- 新增 forwardRuntimeCompatPlugin 插件,处理 node:async_hooks 的浏览器兼容实现,并重定向 brotli 字典文件至浏览器版本,确保 ForwardWidget 在浏览器类运行时环境中正常运行
- 将 iQiyi 源中的 Brotli 解压逻辑迁移至 codec-util 的 decompressBrotli 通用函数,统一使用 utf8BytesToString 替代 TextDecoder,提升代码复用性和可维护性
- 此举旨在减少构建产物中的 Node.js 内置模块依赖,并集中管理解码工具,便于后续扩展
2026-07-18 08:08:12 +08:00
lilixu3 64f885c98d fix: 芒果TV搜索API改用新版v2接口+seqId签名 (#374)
* fix: 芒果TV搜索API改用新版v2接口+seqId签名

旧版 /msite/search/v2 已返回403,改用 /aphone/search/rebirth/v2
新增 seqId=md5(deviceId + "." + timestamp) 签名
适配新版响应格式 mediaRebirthV2

* 使用纯JS md5替代Node crypto模块以兼容fw播放器
2026-07-17 07:27:47 +08:00