karin-plugin-kkk 2.32.3 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/lib/build-metadata.json +5 -5
- package/lib/core_chunk/amagi.d.mts +187 -1739
- package/lib/core_chunk/main.js +86 -15
- package/lib/core_chunk/template.d.mts +11 -0
- package/lib/core_chunk/vendor.js +779 -756
- package/lib/karin-plugin-kkk.css +46 -0
- package/package.json +2 -2
package/lib/core_chunk/main.js
CHANGED
|
@@ -18863,20 +18863,50 @@ var AmbientBackground$1 = import_react.memo(({ pic }) => /* @__PURE__ */ (0, imp
|
|
|
18863
18863
|
]
|
|
18864
18864
|
}));
|
|
18865
18865
|
AmbientBackground$1.displayName = "AmbientBackground";
|
|
18866
|
+
var MAX_DANMAKU_ROWS = 4;
|
|
18867
|
+
/**
|
|
18868
|
+
* 封面弹幕层:模拟视频平台截图中的滚动弹幕轨道,行数由 MAX_DANMAKU_ROWS 控制。
|
|
18869
|
+
*/
|
|
18870
|
+
var DanmakuOverlay = import_react.memo(({ items }) => {
|
|
18871
|
+
if (!items.length) return null;
|
|
18872
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
18873
|
+
className: "pointer-events-none absolute inset-x-0 top-0 z-20 overflow-hidden px-16",
|
|
18874
|
+
style: {
|
|
18875
|
+
height: `calc(${MAX_DANMAKU_ROWS} * 20rem)`,
|
|
18876
|
+
maskImage: "linear-gradient(to right, transparent 0, black 3rem, black calc(100% - 10rem), transparent 100%)",
|
|
18877
|
+
WebkitMaskImage: "linear-gradient(to right, transparent 0, black 3rem, black calc(100% - 10rem), transparent 100%)"
|
|
18878
|
+
},
|
|
18879
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
18880
|
+
className: "relative pt-8 overflow-visible whitespace-normal text-[0] leading-16",
|
|
18881
|
+
children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
18882
|
+
className: "mr-20 inline-block shrink-0 whitespace-nowrap align-top text-4xl font-black leading-16 text-white",
|
|
18883
|
+
style: {
|
|
18884
|
+
marginLeft: index === 0 ? void 0 : `${(index * 5 + item.content.length * 3) % 5 * 2}rem`,
|
|
18885
|
+
opacity: Math.max(.2, 1 - index * .025),
|
|
18886
|
+
textShadow: "0 3px 8px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.95)",
|
|
18887
|
+
WebkitTextStroke: "1px rgba(0,0,0,0.58)"
|
|
18888
|
+
},
|
|
18889
|
+
children: item.content
|
|
18890
|
+
}, `${item.content}-${index}`))
|
|
18891
|
+
})
|
|
18892
|
+
});
|
|
18893
|
+
});
|
|
18894
|
+
DanmakuOverlay.displayName = "DanmakuOverlay";
|
|
18866
18895
|
var BilibiliVideoInfo = import_react.memo((props) => {
|
|
18867
18896
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DefaultLayout, {
|
|
18868
18897
|
...props,
|
|
18869
18898
|
className: "relative overflow-hidden",
|
|
18870
18899
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AmbientBackground$1, { pic: props.data.pic }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
18871
18900
|
className: "relative z-10",
|
|
18872
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.
|
|
18901
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
18902
|
+
className: "relative overflow-hidden",
|
|
18873
18903
|
style: coverMaskStyle$1,
|
|
18874
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnhancedImage, {
|
|
18904
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EnhancedImage, {
|
|
18875
18905
|
src: props.data.pic,
|
|
18876
18906
|
alt: props.data.title,
|
|
18877
18907
|
className: "object-cover w-full",
|
|
18878
18908
|
placeholder: "视频封面"
|
|
18879
|
-
})
|
|
18909
|
+
}), props.data.hotDanmaku && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DanmakuOverlay, { items: props.data.hotDanmaku })]
|
|
18880
18910
|
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
18881
18911
|
className: "flex flex-col gap-10 px-16 pt-20",
|
|
18882
18912
|
children: [
|
|
@@ -35288,6 +35318,30 @@ async function getBiliFrameRate(path) {
|
|
|
35288
35318
|
} catch {}
|
|
35289
35319
|
return 30;
|
|
35290
35320
|
}
|
|
35321
|
+
/**
|
|
35322
|
+
* 统计用于视频信息封面展示的弹幕(相同内容聚合)
|
|
35323
|
+
* @param danmakuList 弹幕列表
|
|
35324
|
+
* @param topN 返回的条数,默认 5
|
|
35325
|
+
* @returns 重复弹幕优先,不足时用真实弹幕按出现顺序补齐
|
|
35326
|
+
*/
|
|
35327
|
+
function getHotDanmaku(danmakuList, topN = 5) {
|
|
35328
|
+
const counter = /* @__PURE__ */ new Map();
|
|
35329
|
+
const firstSeen = /* @__PURE__ */ new Map();
|
|
35330
|
+
const sortedDanmaku = [...danmakuList].sort((a, b) => a.progress - b.progress);
|
|
35331
|
+
for (const dm of sortedDanmaku) {
|
|
35332
|
+
const content = dm.content?.trim();
|
|
35333
|
+
if (!content) continue;
|
|
35334
|
+
if (!firstSeen.has(content)) firstSeen.set(content, dm.progress);
|
|
35335
|
+
counter.set(content, (counter.get(content) ?? 0) + 1);
|
|
35336
|
+
}
|
|
35337
|
+
return [...counter.entries()].map(([content, count]) => ({
|
|
35338
|
+
content,
|
|
35339
|
+
count
|
|
35340
|
+
})).sort((a, b) => {
|
|
35341
|
+
if (b.count !== a.count) return b.count - a.count;
|
|
35342
|
+
return (firstSeen.get(a.content) ?? 0) - (firstSeen.get(b.content) ?? 0);
|
|
35343
|
+
}).slice(0, topN);
|
|
35344
|
+
}
|
|
35291
35345
|
/** 字号配置映射 */
|
|
35292
35346
|
var FONT_SIZE_MAP$1 = {
|
|
35293
35347
|
small: {
|
|
@@ -36057,6 +36111,9 @@ var Bilibili = class extends Base {
|
|
|
36057
36111
|
host_mid: infoData.data.data.owner.mid,
|
|
36058
36112
|
typeMode: "strict"
|
|
36059
36113
|
});
|
|
36114
|
+
const danmakuCid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
|
|
36115
|
+
const danmakuDuration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
|
|
36116
|
+
const hotDanmaku = getHotDanmaku(await this.fetchVideoDanmakuList(danmakuCid, danmakuDuration), 20);
|
|
36060
36117
|
const img = await Render(this.e, "bilibili/videoInfo", {
|
|
36061
36118
|
share_url: "https://b23.tv/" + infoData.data.data.bvid,
|
|
36062
36119
|
title: infoData.data.data.title,
|
|
@@ -36065,6 +36122,7 @@ var Bilibili = class extends Base {
|
|
|
36065
36122
|
bvid: infoData.data.data.bvid,
|
|
36066
36123
|
ctime: infoData.data.data.ctime,
|
|
36067
36124
|
pic: infoData.data.data.pic,
|
|
36125
|
+
hotDanmaku,
|
|
36068
36126
|
owner: {
|
|
36069
36127
|
...infoData.data.data.owner,
|
|
36070
36128
|
usernameMeta: getUsernameMetadata(userProfileData.data.data.card),
|
|
@@ -36140,20 +36198,10 @@ var Bilibili = class extends Base {
|
|
|
36140
36198
|
else {
|
|
36141
36199
|
if (Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64) this.islogin = false;
|
|
36142
36200
|
let danmakuList = [];
|
|
36143
|
-
if (this.forceBurnDanmaku || Config.bilibili.burnDanmaku)
|
|
36201
|
+
if (this.forceBurnDanmaku || Config.bilibili.burnDanmaku) {
|
|
36144
36202
|
const cid = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid : infoData.data.data.cid;
|
|
36145
36203
|
const duration = iddata.p ? infoData.data.data.pages[iddata.p - 1]?.duration ?? infoData.data.data.duration : infoData.data.data.duration;
|
|
36146
|
-
|
|
36147
|
-
logger.debug(`视频时长: ${duration}秒, 需要获取 ${segmentCount} 个弹幕分段`);
|
|
36148
|
-
const danmakuPromises = Array.from({ length: segmentCount }, (_, i) => this.amagi.bilibili.fetcher.fetchVideoDanmaku({
|
|
36149
|
-
cid,
|
|
36150
|
-
segment_index: i + 1,
|
|
36151
|
-
typeMode: "strict"
|
|
36152
|
-
}).then((res) => res.data?.data?.elems || []).catch(() => []));
|
|
36153
|
-
danmakuList = (await Promise.all(danmakuPromises)).flat();
|
|
36154
|
-
logger.debug(`获取到 ${danmakuList.length} 条弹幕(${segmentCount} 个分段)`);
|
|
36155
|
-
} catch (err) {
|
|
36156
|
-
logger.warn("获取弹幕失败,将不烧录弹幕", err);
|
|
36204
|
+
danmakuList = await this.fetchVideoDanmakuList(cid, duration);
|
|
36157
36205
|
}
|
|
36158
36206
|
await this.getvideo(Config.bilibili.videoQuality !== 0 && Config.bilibili.videoQuality < 64 ? {
|
|
36159
36207
|
playUrlData: nockData.data,
|
|
@@ -36821,6 +36869,29 @@ var Bilibili = class extends Base {
|
|
|
36821
36869
|
default: break;
|
|
36822
36870
|
}
|
|
36823
36871
|
}
|
|
36872
|
+
/**
|
|
36873
|
+
* 获取视频弹幕列表(按每 6 分钟一段并行拉取所有分段)
|
|
36874
|
+
* @param cid 视频分P的 cid
|
|
36875
|
+
* @param duration 视频时长(秒)
|
|
36876
|
+
* @returns 合并后的弹幕列表
|
|
36877
|
+
*/
|
|
36878
|
+
async fetchVideoDanmakuList(cid, duration) {
|
|
36879
|
+
try {
|
|
36880
|
+
const segmentCount = Math.ceil(duration / 360);
|
|
36881
|
+
logger.debug(`视频时长: ${duration}秒, 需要获取 ${segmentCount} 个弹幕分段`);
|
|
36882
|
+
const danmakuPromises = Array.from({ length: segmentCount }, (_, i) => this.amagi.bilibili.fetcher.fetchVideoDanmaku({
|
|
36883
|
+
cid,
|
|
36884
|
+
segment_index: i + 1,
|
|
36885
|
+
typeMode: "strict"
|
|
36886
|
+
}).then((res) => res.data?.data?.elems || []).catch(() => []));
|
|
36887
|
+
const danmakuList = (await Promise.all(danmakuPromises)).flat();
|
|
36888
|
+
logger.debug(`获取到 ${danmakuList.length} 条弹幕(${segmentCount} 个分段)`);
|
|
36889
|
+
return danmakuList;
|
|
36890
|
+
} catch (err) {
|
|
36891
|
+
logger.warn("获取弹幕失败", err);
|
|
36892
|
+
return [];
|
|
36893
|
+
}
|
|
36894
|
+
}
|
|
36824
36895
|
async getvideo({ infoData, playUrlData, danmakuList = [] }) {
|
|
36825
36896
|
/** 获取视频 => FFmpeg合成 */
|
|
36826
36897
|
logger.debug("是否登录:", this.islogin);
|
|
@@ -1257,6 +1257,15 @@ interface BilibiliVideoOwner {
|
|
|
1257
1257
|
/** 头像框图片URL */
|
|
1258
1258
|
frame?: string;
|
|
1259
1259
|
}
|
|
1260
|
+
/**
|
|
1261
|
+
* 热门弹幕项接口(相同内容的弹幕聚合)
|
|
1262
|
+
*/
|
|
1263
|
+
interface BilibiliHotDanmaku {
|
|
1264
|
+
/** 弹幕内容 */
|
|
1265
|
+
content: string;
|
|
1266
|
+
/** 出现次数 */
|
|
1267
|
+
count: number;
|
|
1268
|
+
}
|
|
1260
1269
|
/**
|
|
1261
1270
|
* B站视频信息数据接口
|
|
1262
1271
|
*/
|
|
@@ -1277,6 +1286,8 @@ interface BilibiliVideoInfoData {
|
|
|
1277
1286
|
pic: string;
|
|
1278
1287
|
/** UP主信息 */
|
|
1279
1288
|
owner: BilibiliVideoOwner;
|
|
1289
|
+
/** 出现次数最多的热门弹幕(可选,按次数降序) */
|
|
1290
|
+
hotDanmaku?: BilibiliHotDanmaku[];
|
|
1280
1291
|
}
|
|
1281
1292
|
/**
|
|
1282
1293
|
* B站视频信息组件属性接口
|