Skip to content

Commit b5d41fb

Browse files
authored
重构韩剧TV弹幕源的核心逻辑,优化双链路(韩小圈 HXQ + 极速版 TV)架构的稳定性和代码质量,修复多个实际运行中的 bug (huangxd-#261)
* feat(hanjutv): 重构韩剧TV源为双链路架构(韩小圈+极速版) - 搜索:并行调用 s5(韩小圈)和 TV(极速版)接口,按标题匹配自动合并为 merged 候选,未匹配的保留为独立变体 - 详情/剧集:merged 变体并行拉取双端剧集列表,按集号合并;支持 3 层降级(merged → hxq → tv) - 弹幕:merged 剧集 ID 编码为 base64url,拉取时按 ref 分别请求并打上细粒度来源标签(韩小圈/极速版) - 弹幕分页:韩小圈端使用 60s 窗口滑动 + 多 host 容灾,TV 端使用全量范围分页 - 加密:统一 AES-CBC 加密参数,新增 TV 端请求头构建(buildLiteHeaders)和响应解密 - 搜索身份:闭包工厂模式缓存 mobile/tv 请求头,暖身请求实例内只执行一次 - 外层合并:fetchMergedComments 保留韩剧TV内部细粒度标签,仅在缺失时补充源标签 - 点赞阈值:handleDanmusLike 支持韩小圈/极速版变体标签的低阈值判定 - 公共工具:base64url 编解码移至 codec-util,正时间戳归一化移至 time-util * 将韩剧TV专用函数从通用工具移到hanjutv-util encodeBase64UrlText、decodeBase64UrlText、normalizePositiveTimestamp 三个函数只有hanjutv-util在用,放在通用工具里会导致部分运行环境 文件体积超限无法加载,移到hanjutv-util作为本地函数
1 parent ae4f20c commit b5d41fb

7 files changed

Lines changed: 737 additions & 248 deletions

File tree

danmu_api/apis/dandan-api.js

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "../utils/common-util.js";
1818
import { getTMDBChineseTitle } from "../utils/tmdb-util.js";
1919
import { applyMergeLogic, mergeDanmakuList, MERGE_DELIMITER, alignSourceTimelines } from "../utils/merge-util.js";
20+
import { getHanjutvSourceLabel } from "../utils/hanjutv-util.js";
2021
import AIClient from '../utils/ai-util.js';
2122
import Kan360Source from "../sources/kan360.js";
2223
import VodSource from "../sources/vod.js";
@@ -1356,8 +1357,36 @@ function buildBangumiData(anime, idParam = "") {
13561357
*/
13571358
async function fetchMergedComments(url, animeTitle, commentId) {
13581359
const parts = url.split(MERGE_DELIMITER);
1359-
const sourceNames = parts.map(part => part.split(':')[0]).filter(Boolean);
1360-
const sourceTag = sourceNames.join('&');
1360+
const partMetas = parts.map((part) => {
1361+
const firstColonIndex = part.indexOf(':');
1362+
if (firstColonIndex === -1) {
1363+
return {
1364+
realId: '',
1365+
logicalSource: '',
1366+
sourceLabel: '',
1367+
};
1368+
}
1369+
1370+
const sourceName = part.substring(0, firstColonIndex);
1371+
const realId = part.substring(firstColonIndex + 1);
1372+
1373+
if (sourceName !== 'hanjutv') {
1374+
return {
1375+
realId,
1376+
logicalSource: sourceName,
1377+
sourceLabel: sourceName,
1378+
};
1379+
}
1380+
1381+
return {
1382+
realId,
1383+
logicalSource: 'hanjutv',
1384+
sourceLabel: getHanjutvSourceLabel(realId),
1385+
};
1386+
});
1387+
const sourceNames = partMetas.map(meta => meta.logicalSource).filter(Boolean);
1388+
const realIds = partMetas.map(meta => meta.realId);
1389+
const sourceTag = partMetas.map(meta => meta.sourceLabel).filter(Boolean).join('&');
13611390

13621391
log("info", `[Merge] 开始获取 [${sourceTag}] 聚合弹幕...`);
13631392

@@ -1371,12 +1400,10 @@ async function fetchMergedComments(url, animeTitle, commentId) {
13711400
const stats = {};
13721401

13731402
// 2. 并行获取所有源的弹幕
1374-
const tasks = parts.map(async (part) => {
1375-
const firstColonIndex = part.indexOf(':');
1376-
if (firstColonIndex === -1) return [];
1377-
1378-
const sourceName = part.substring(0, firstColonIndex);
1379-
const realId = part.substring(firstColonIndex + 1);
1403+
const tasks = partMetas.map(async (meta) => {
1404+
const sourceName = meta.logicalSource;
1405+
const sourceLabel = meta.sourceLabel || meta.logicalSource;
1406+
const realId = meta.realId;
13801407

13811408
if (!sourceName || !realId) return [];
13821409

@@ -1424,19 +1451,21 @@ async function fetchMergedComments(url, animeTitle, commentId) {
14241451

14251452
// 给合并工具里的每一条弹幕打上独立的原始源标签
14261453
if (formatted && Array.isArray(formatted)) {
1427-
formatted.forEach(item => item._sourceLabel = sourceName);
1454+
formatted.forEach(item => {
1455+
if (!item._sourceLabel) item._sourceLabel = sourceLabel;
1456+
});
14281457
}
14291458

14301459
// 提取并挂载 dandan 源传出的精确偏移量
14311460
if (raw && raw.relatedShifts) {
14321461
formatted.relatedShifts = raw.relatedShifts;
14331462
}
14341463

1435-
stats[sourceName] = formatted.length;
1464+
stats[sourceLabel] = formatted.length;
14361465
return formatted;
14371466
} catch (e) {
1438-
log("error", `[Merge] 获取 ${sourceName} 失败: ${e.message}`);
1439-
stats[sourceName] = 0;
1467+
log("error", `[Merge] 获取 ${sourceLabel} 失败: ${e.message}`);
1468+
stats[sourceLabel] = 0;
14401469
return [];
14411470
}
14421471
}
@@ -1459,11 +1488,6 @@ async function fetchMergedComments(url, animeTitle, commentId) {
14591488

14601489
// 跨源时间轴对齐(仅当存在 dandan 源时执行)
14611490
if (sourceNames.includes('dandan')) {
1462-
const realIds = parts.map(part => {
1463-
const firstColonIndex = part.indexOf(':');
1464-
return firstColonIndex === -1 ? '' : part.substring(firstColonIndex + 1);
1465-
});
1466-
14671491
// 提取精确偏移集合
14681492
const dandanIndex = sourceNames.indexOf('dandan');
14691493
const dandanShifts = (results[dandanIndex] && results[dandanIndex].relatedShifts) ? results[dandanIndex].relatedShifts : {};

0 commit comments

Comments
 (0)