koishi-plugin-kkk 3.4.2 → 3.6.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 +105 -0
- package/assets/web/assets/index-BRROK0vh.js +1 -1
- package/config/default_config/app.yaml +4 -6
- package/config/default_config/bilibili.yaml +1 -1
- package/config/default_config/config.json +2 -2
- package/lib/build-metadata.json +10 -0
- package/lib/compat/adapter-info.js +333 -0
- package/lib/compat/forward-collect.js +57 -4
- package/lib/compat/node-karin.js +25 -56
- package/lib/compat/sendError.js +27 -0
- package/lib/index.js +15 -8
- package/lib/karin/apps/help.js +23 -2
- package/lib/karin/module/utils/Base.js +30 -8
- package/lib/karin/module/utils/ErrorHandler/render.js +8 -2
- package/lib/karin/module/utils/InteractiveGraph.js +164 -0
- package/lib/karin/module/utils/InteractiveVideo.js +316 -0
- package/lib/karin/module/utils/Network/Downloader.js +159 -13
- package/lib/karin/module/utils/Network/Network.js +5 -1
- package/lib/karin/module/utils/ParseForward.js +12 -2
- package/lib/karin/module/utils/ParseSteps.js +1 -1
- package/lib/karin/module/utils/Render/index.js +10 -110
- package/lib/karin/module/utils/build-metadata.js +64 -19
- package/lib/karin/module/utils/runtime-report.js +16 -4
- package/lib/karin/platform/bilibili/bilibili.js +137 -11
- package/lib/karin/platform/bilibili/interactive-story.js +463 -0
- package/lib/karin/platform/bilibili/push.js +12 -5
- package/lib/ktr/registry.js +1 -0
- package/lib/ktr/template/bilibili/interactive/components/interactive.js +137 -0
- package/lib/ktr/template/bilibili/interactive/components/types.js +3 -0
- package/lib/ktr/template/bilibili/interactive/index.js +13 -0
- package/lib/ktr/template/other/handlerError/components/handlerError.js +1 -1
- package/lib/webui.js +40 -18
- package/package.json +1 -1
- package/scripts/build.mjs +36 -0
- package/scripts/patch-webui.mjs +126 -40
- package/scripts/probe-edge-cookie.cjs +50 -0
- package/scripts/probe-panel-options.cjs +15 -0
- package/scripts/probe-schema-options.cjs +12 -0
- package/scripts/smoke-adapter-info.cjs +149 -0
- package/scripts/smoke-config-schema.cjs +62 -0
- package/scripts/smoke-download-source-fallback.cjs +166 -0
- package/scripts/smoke-forward-chart.cjs +139 -0
- package/scripts/smoke-interactive-card.cjs +87 -0
- package/scripts/smoke-interactive-story.cjs +285 -0
- package/scripts/smoke-interactive.cjs +298 -0
- package/scripts/smoke-onebot-forward.cjs +10 -5
- package/scripts/smoke-panel-chart.cjs +92 -0
- package/scripts/smoke-parse-forward.cjs +1 -1
- package/scripts/smoke-render-path.cjs +44 -55
- package/src/compat/adapter-info.ts +346 -0
- package/src/compat/forward-collect.ts +69 -4
- package/src/compat/node-karin.ts +24 -54
- package/src/compat/sendError.ts +26 -0
- package/src/index.ts +15 -8
- package/src/karin/apps/help.ts +26 -3
- package/src/karin/module/utils/Base.ts +39 -9
- package/src/karin/module/utils/ErrorHandler/render.ts +8 -2
- package/src/karin/module/utils/InteractiveGraph.ts +199 -0
- package/src/karin/module/utils/InteractiveVideo.ts +400 -0
- package/src/karin/module/utils/Network/Downloader.ts +164 -14
- package/src/karin/module/utils/Network/Network.ts +6 -2
- package/src/karin/module/utils/ParseForward.ts +12 -3
- package/src/karin/module/utils/ParseSteps.ts +1 -1
- package/src/karin/module/utils/Render/index.ts +10 -113
- package/src/karin/module/utils/build-metadata.ts +61 -19
- package/src/karin/module/utils/runtime-report.ts +16 -4
- package/src/karin/platform/bilibili/bilibili.ts +149 -20
- package/src/karin/platform/bilibili/interactive-story.ts +519 -0
- package/src/karin/platform/bilibili/push.ts +12 -6
- package/src/karin/types/config/app.ts +7 -15
- package/src/karin/types/config/bilibili.ts +1 -1
- package/src/karin/types/netrorks/NetworksConfigType.ts +5 -0
- package/src/ktr/registry.ts +1 -0
- package/src/ktr/template/bilibili/interactive/components/interactive.tsx +202 -0
- package/src/ktr/template/bilibili/interactive/components/types.ts +37 -0
- package/src/ktr/template/bilibili/interactive/index.tsx +15 -0
- package/src/ktr/template/other/handlerError/components/handlerError.tsx +4 -1
- package/src/webui.ts +38 -16
- package/scripts/smoke-shotkit-compat.cjs +0 -102
|
@@ -8,6 +8,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const node_karin_1 = require("../../../compat/node-karin.js");
|
|
11
|
+
const adapter_info_1 = require("../../../compat/adapter-info");
|
|
11
12
|
const root_1 = require("../../root");
|
|
12
13
|
const build_metadata_1 = require("./build-metadata");
|
|
13
14
|
const Config_1 = require("./Config");
|
|
@@ -60,10 +61,16 @@ exports.getLocalChangelog = getLocalChangelog;
|
|
|
60
61
|
* 不采集账号、主机名、用户目录、网络地址、环境变量内容、启动参数或适配器鉴权信息,
|
|
61
62
|
* 保证该命令在群聊中触发时不会把机器身份和凭据写入图片。
|
|
62
63
|
*
|
|
64
|
+
* 采集前会尝试问一句适配器的实现端版本(NapCat / Lagrange 这类),带 1.5 秒超时,
|
|
65
|
+
* 问不到就退化成「适配器插件版本」,绝不让海报卡住。
|
|
66
|
+
*
|
|
63
67
|
* @param event 当前消息事件
|
|
64
68
|
*/
|
|
65
|
-
const collectRuntimeReport = (event) => {
|
|
69
|
+
const collectRuntimeReport = async (event) => {
|
|
66
70
|
const packageMetadata = root_1.Root.pkg;
|
|
71
|
+
// 兼容层里 e.bot 是包装对象,真实 Koishi Bot 在 .bot 上:问实现端版本要用真的那个
|
|
72
|
+
const koishiBot = event.bot?.bot ?? event.bot;
|
|
73
|
+
await (0, adapter_info_1.queryAdapterImplementation)(koishiBot);
|
|
67
74
|
const adapter = event.bot.adapter;
|
|
68
75
|
const cpus = node_os_1.default.cpus();
|
|
69
76
|
const memory = process.memoryUsage();
|
|
@@ -72,7 +79,10 @@ const collectRuntimeReport = (event) => {
|
|
|
72
79
|
const buildMetadata = (0, build_metadata_1.getBuildMetadata)();
|
|
73
80
|
const currentChangelog = (0, exports.getLocalChangelog)(1);
|
|
74
81
|
const renderScale = Math.min(2, Math.max(0.5, Number(Config_1.Config.app.renderScale) / 100));
|
|
75
|
-
|
|
82
|
+
// 没有构建产物(老安装包)时依然会拿到一份 package.json 兜底元数据,所以用「有没有构建时间」判断
|
|
83
|
+
const buildState = !buildMetadata.buildTime
|
|
84
|
+
? 'unavailable'
|
|
85
|
+
: buildMetadata.version === root_1.Root.pluginVersion ? 'matched' : 'mismatched';
|
|
76
86
|
return {
|
|
77
87
|
snapshotAt: new Intl.DateTimeFormat('zh-CN', {
|
|
78
88
|
year: 'numeric',
|
|
@@ -109,13 +119,15 @@ const collectRuntimeReport = (event) => {
|
|
|
109
119
|
processUptime: formatDuration(process.uptime())
|
|
110
120
|
},
|
|
111
121
|
adapter: {
|
|
112
|
-
|
|
122
|
+
// 卡片上要好看:NapCat / OneBot 这种;取不到就退回平台名
|
|
123
|
+
name: adapter.displayName || adapter.name || '未知',
|
|
113
124
|
version: adapter.version || '未知',
|
|
114
125
|
platform: String(adapter.platform || '未知'),
|
|
115
126
|
protocol: String(adapter.protocol || '未知'),
|
|
116
127
|
standard: String(adapter.standard || '未知'),
|
|
117
128
|
communication: String(adapter.communication || '未知'),
|
|
118
|
-
|
|
129
|
+
// connectTime 为 0 表示「没记到上线时间」,此时别拿 1970 年去算天数
|
|
130
|
+
connectedFor: adapter.connectTime > 0 ? formatDuration(Math.max(0, (Date.now() - adapter.connectTime) / 1000)) : '未知'
|
|
119
131
|
},
|
|
120
132
|
renderer: {
|
|
121
133
|
scale: `${renderScale.toFixed(2)}x`,
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.extractArticleImages = exports.getvideosize = exports.bilibiliProcessVideos = exports.preferAvcStream = exports.isAvcStream = exports.parseAdditionalCard = exports.generateDecorationCard = exports.cover = exports.TimeFormatter = exports.Bilibili = void 0;
|
|
39
|
+
exports.extractArticleImages = exports.getvideosize = exports.bilibiliProcessVideos = exports.preferAvcStream = exports.isAvcStream = exports.parseAdditionalCard = exports.generateDecorationCard = exports.cover = exports.TimeFormatter = exports.Bilibili = exports.buildDashUrlCandidates = void 0;
|
|
40
40
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
41
41
|
const ImageSlice_1 = require("../../module/utils/ImageSlice");
|
|
42
42
|
const QqPanel_1 = require("../../module/utils/QqPanel");
|
|
@@ -55,6 +55,7 @@ const QqPanel_2 = require("../../module/utils/QqPanel");
|
|
|
55
55
|
const utils_1 = require("../../module/utils");
|
|
56
56
|
const amagiClient_1 = require("../../module/utils/amagiClient");
|
|
57
57
|
const Config_1 = require("../../module/utils/Config");
|
|
58
|
+
const InteractiveVideo_1 = require("../../module/utils/InteractiveVideo");
|
|
58
59
|
const ParseOverride_1 = require("../../module/utils/ParseOverride");
|
|
59
60
|
// 解析阶段(「下载进度」指令读的就是这里登记的状态)
|
|
60
61
|
const Downloader_1 = require("../../module/utils/Network/Downloader");
|
|
@@ -62,7 +63,44 @@ const parseTip_1 = require("../../module/utils/parseTip");
|
|
|
62
63
|
const bilibili_1 = require("../../platform/bilibili");
|
|
63
64
|
const danmaku_1 = require("../../platform/bilibili/danmaku");
|
|
64
65
|
const dynamic_text_1 = require("../../platform/bilibili/dynamic-text");
|
|
66
|
+
const interactive_story_1 = require("../../platform/bilibili/interactive-story");
|
|
65
67
|
let img;
|
|
68
|
+
/**
|
|
69
|
+
* 一个地址是不是 PCDN 边缘节点。
|
|
70
|
+
*
|
|
71
|
+
* `xy120x240x109x22xy.mcdn.bilivideo.cn:8082/v1/resource/upgcxcode/...` 这种就是:
|
|
72
|
+
* 域名带 mcdn、带非标准端口、路径 `/v1/resource/`。
|
|
73
|
+
* 它们对本机来说常常是「TCP 连得上、响应头也正常,就是不吐数据」(实测 118 KB / 60 秒),
|
|
74
|
+
* 而同一份数据在 `upos-*` 镜像上能跑满带宽。
|
|
75
|
+
*/
|
|
76
|
+
const isPcdnUrl = (url) => /\.mcdn\.bilivideo\./i.test(url) || /\.pcdn\./i.test(url) || /\/v1\/resource\//i.test(url);
|
|
77
|
+
/**
|
|
78
|
+
* 把一条 dash 流的直链整理成**按优先级排序**的候选列表。
|
|
79
|
+
*
|
|
80
|
+
* 排序规则(尽量不改变原来的行为,只在必要时动):
|
|
81
|
+
* 1. `base_url` 不是 PCDN 时保持第一 —— 原来的行为不变;
|
|
82
|
+
* 2. `base_url` 是 PCDN 时,把非 PCDN 的备用地址提到前面;
|
|
83
|
+
* 3. 其余地址按接口给的顺序跟在后面。
|
|
84
|
+
*
|
|
85
|
+
* 下载器遇到「连得上但不吐数据」的源会自动换到下一个(见 Downloader 的坏源看门狗),
|
|
86
|
+
* 所以列表顺序就是换源顺序。
|
|
87
|
+
*
|
|
88
|
+
* @param stream dash 里的 video / audio 流对象
|
|
89
|
+
* @returns 去重且已排序的直链列表(可能为空)
|
|
90
|
+
*/
|
|
91
|
+
const buildDashUrlCandidates = (stream) => {
|
|
92
|
+
const raw = [stream?.base_url ?? stream?.baseUrl, ...(stream?.backup_url ?? stream?.backupUrl ?? [])];
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
const urls = [];
|
|
95
|
+
for (const url of raw) {
|
|
96
|
+
if (typeof url !== 'string' || !/^https?:\/\//i.test(url) || seen.has(url))
|
|
97
|
+
continue;
|
|
98
|
+
seen.add(url);
|
|
99
|
+
urls.push(url);
|
|
100
|
+
}
|
|
101
|
+
return [...urls.filter((url) => !isPcdnUrl(url)), ...urls.filter(isPcdnUrl)];
|
|
102
|
+
};
|
|
103
|
+
exports.buildDashUrlCandidates = buildDashUrlCandidates;
|
|
66
104
|
/** 评论请求统一使用匿名态,避免账号 Cookie 改变评论热度池结果。 */
|
|
67
105
|
const bilibiliAnonymousRequestConfig = {
|
|
68
106
|
headers: {
|
|
@@ -90,6 +128,10 @@ class Bilibili extends utils_1.Base {
|
|
|
90
128
|
downloadfilename;
|
|
91
129
|
/** 强制烧录弹幕(用于 #弹幕解析 命令) */
|
|
92
130
|
forceBurnDanmaku;
|
|
131
|
+
/** 只发这一段视频:互动剧情续播用,不发作品卡与评论区 */
|
|
132
|
+
storyOnly;
|
|
133
|
+
/** 本次是不是互动视频;是的话视频发完要发剧情选项 */
|
|
134
|
+
interactive;
|
|
93
135
|
/**
|
|
94
136
|
* 本次解析取到的弹幕。
|
|
95
137
|
*
|
|
@@ -113,6 +155,7 @@ class Bilibili extends utils_1.Base {
|
|
|
113
155
|
super(e);
|
|
114
156
|
this.e = e;
|
|
115
157
|
this.isVIP = false;
|
|
158
|
+
this.storyOnly = options?.storyOnly ?? false;
|
|
116
159
|
this.Type = data?.type;
|
|
117
160
|
this.workType = BILIBILI_WORK_TYPES[data?.type];
|
|
118
161
|
this.islogin = data?.USER?.STATUS === 'isLogin';
|
|
@@ -134,7 +177,7 @@ class Bilibili extends utils_1.Base {
|
|
|
134
177
|
// replyReplacing 会先撤掉上一条(也就是刚点的画质面板),群里只留这句提示
|
|
135
178
|
await (0, QqPanel_2.replyReplacing)(this.e, (0, QqPanel_2.buildDownloadTip)(String(iddata.bvid ?? ''), '收到请求,开始下载'));
|
|
136
179
|
}
|
|
137
|
-
else if (Config_1.Config.app.parseTip) {
|
|
180
|
+
else if (!this.storyOnly && Config_1.Config.app.parseTip) {
|
|
138
181
|
/**
|
|
139
182
|
* 同样:发这句话时把上一条机器人消息撤掉。
|
|
140
183
|
* 另外过一道「同一句提示 5 秒内只发一次」—— 同一条消息被投递多遍时
|
|
@@ -180,9 +223,18 @@ class Bilibili extends utils_1.Base {
|
|
|
180
223
|
durationSeconds: Number(detail.duration) > 0 ? Number(detail.duration) : undefined
|
|
181
224
|
};
|
|
182
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* 本次要解析的目标 cid:普通视频按分 P / 主 cid;互动剧情的续播节点由上层直接指定 cid。
|
|
228
|
+
*/
|
|
229
|
+
const targetCid = Number(iddata.cid ?? (iddata.p ? (infoData.data.data.pages[iddata.p - 1]?.cid ?? infoData.data.data.cid) : infoData.data.data.cid));
|
|
230
|
+
/** 互动视频(stein gate):视频发完要发剧情选项,续播的那几段不再重复识别 */
|
|
231
|
+
if (!this.storyOnly && Number(infoData.data.data.rights?.is_stein_gate) === 1) {
|
|
232
|
+
this.interactive = { bvid: String(iddata.bvid ?? infoData.data.data.bvid ?? ''), cid: targetCid };
|
|
233
|
+
node_karin_1.logger.info('[互动视频] 检测到互动视频(bv=' + this.interactive.bvid + ' cid=' + targetCid + ')');
|
|
234
|
+
}
|
|
183
235
|
const playUrlData = await this.amagi.bilibili.fetcher.fetchVideoStreamUrl({
|
|
184
236
|
avid: infoData.data.data.aid,
|
|
185
|
-
cid:
|
|
237
|
+
cid: targetCid
|
|
186
238
|
});
|
|
187
239
|
// const playUrl = bilibiliApiUrls.视频流信息({ avid: infoData.data.aid, cid: infoData.data.cid })
|
|
188
240
|
/**
|
|
@@ -209,7 +261,7 @@ class Bilibili extends utils_1.Base {
|
|
|
209
261
|
const nockData = (await new utils_1.Networks({
|
|
210
262
|
url: amagi_1.bilibiliApiUrls.getVideoStream({
|
|
211
263
|
avid: infoData.data.data.aid,
|
|
212
|
-
cid:
|
|
264
|
+
cid: targetCid
|
|
213
265
|
}) + '&platform=html5',
|
|
214
266
|
headers: this.headers
|
|
215
267
|
}).getData());
|
|
@@ -221,7 +273,7 @@ class Bilibili extends utils_1.Base {
|
|
|
221
273
|
*/
|
|
222
274
|
// fromPanel:面板里已经发过这张卡片了,别再发一遍
|
|
223
275
|
const renderInfoCard = async () => {
|
|
224
|
-
if (fromPanel || !Config_1.Config.bilibili.sendContent.some((content) => content === 'info'))
|
|
276
|
+
if (this.storyOnly || fromPanel || !Config_1.Config.bilibili.sendContent.some((content) => content === 'info'))
|
|
225
277
|
return;
|
|
226
278
|
if (Config_1.Config.bilibili.videoInfoMode === 'text') {
|
|
227
279
|
// 构建回复内容数组
|
|
@@ -419,7 +471,7 @@ class Bilibili extends utils_1.Base {
|
|
|
419
471
|
sends.add('渲染作品信息卡', renderInfoCard);
|
|
420
472
|
// 评论区同样只跳过自身
|
|
421
473
|
sends.add('渲染评论区', async () => {
|
|
422
|
-
if (!Config_1.Config.bilibili.sendContent.some((content) => content === 'comment'))
|
|
474
|
+
if (this.storyOnly || !Config_1.Config.bilibili.sendContent.some((content) => content === 'comment'))
|
|
423
475
|
return;
|
|
424
476
|
const commentsData = await (0, amagiClient_1.softFetch)(() => this.amagi.bilibili.fetcher.fetchComments({
|
|
425
477
|
number: Config_1.Config.bilibili.numcomment,
|
|
@@ -506,6 +558,9 @@ class Bilibili extends utils_1.Base {
|
|
|
506
558
|
sends.add('发送视频', async () => {
|
|
507
559
|
await downloadTask;
|
|
508
560
|
await this.sendPreparedVideo();
|
|
561
|
+
/** 互动视频:视频发完再发剧情(后台跑,不阻塞解析主流程) */
|
|
562
|
+
if (this.interactive)
|
|
563
|
+
void this.startInteractiveStory();
|
|
509
564
|
});
|
|
510
565
|
}
|
|
511
566
|
}
|
|
@@ -1340,15 +1395,24 @@ class Bilibili extends utils_1.Base {
|
|
|
1340
1395
|
this.danmakuList = danmakuList;
|
|
1341
1396
|
switch (this.islogin) {
|
|
1342
1397
|
case true: {
|
|
1343
|
-
|
|
1398
|
+
const videoStream = this.Type === 'one_video' ? playUrlData.data?.dash?.video[0] : playUrlData.result.dash.video[0];
|
|
1399
|
+
node_karin_1.logger.debug('视频 URL:', videoStream?.base_url);
|
|
1344
1400
|
// B站 CDN 需要正确的 Referer
|
|
1345
1401
|
const downloadHeaders = {
|
|
1346
1402
|
...this.headers,
|
|
1347
1403
|
Referer: 'https://www.bilibili.com'
|
|
1348
1404
|
};
|
|
1349
|
-
|
|
1405
|
+
/**
|
|
1406
|
+
* 直链候选:`base_url`(常是 mcdn/PCDN 节点)+ `backup_url` 镜像。
|
|
1407
|
+
* 排在前面的先试,坏源由下载器自动换掉 —— 不然一个死节点就能让整个解析卡死。
|
|
1408
|
+
*/
|
|
1409
|
+
const videoUrls = (0, exports.buildDashUrlCandidates)(videoStream);
|
|
1410
|
+
if (videoUrls.length === 0)
|
|
1411
|
+
throw new Error('没有拿到视频流直链(playurl 返回里没有 base_url)');
|
|
1412
|
+
const bmp4Raw = await (0, utils_1.downloadFile)(videoUrls[0], {
|
|
1350
1413
|
title: `Bil_V_${this.Type === 'one_video' ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.m4s`,
|
|
1351
|
-
headers: downloadHeaders
|
|
1414
|
+
headers: downloadHeaders,
|
|
1415
|
+
backupUrls: videoUrls.slice(1)
|
|
1352
1416
|
});
|
|
1353
1417
|
// 修复 m4s 文件为标准 MP4
|
|
1354
1418
|
const videoPath = utils_1.Common.tempDri.video +
|
|
@@ -1360,14 +1424,18 @@ class Bilibili extends utils_1.Base {
|
|
|
1360
1424
|
}
|
|
1361
1425
|
// 删除原始 m4s 文件
|
|
1362
1426
|
await utils_1.Common.removeFile(bmp4Raw.filepath, true);
|
|
1363
|
-
const
|
|
1427
|
+
const audioStream = this.Type === 'one_video' ? playUrlData.data?.dash?.audio?.[0] : playUrlData.result.dash.audio?.[0];
|
|
1428
|
+
/** 音频候选同样带镜像,理由见上面视频那段 */
|
|
1429
|
+
const audioUrls = (0, exports.buildDashUrlCandidates)(audioStream);
|
|
1430
|
+
const audioUrl = audioUrls[0];
|
|
1364
1431
|
node_karin_1.logger.debug('音频 URL:', audioUrl);
|
|
1365
1432
|
/** 没有音频流(如纯视频稿件)时为 undefined,此时无从合成,直接发视频流 */
|
|
1366
1433
|
let bmp3;
|
|
1367
1434
|
if (audioUrl) {
|
|
1368
1435
|
const bmp3Raw = await (0, utils_1.downloadFile)(audioUrl, {
|
|
1369
1436
|
title: `Bil_A_${this.Type === 'one_video' ? infoData && infoData.data.bvid : infoData && infoData.result.season_id}.m4s`,
|
|
1370
|
-
headers: downloadHeaders
|
|
1437
|
+
headers: downloadHeaders,
|
|
1438
|
+
backupUrls: audioUrls.slice(1)
|
|
1371
1439
|
});
|
|
1372
1440
|
// 修复音频 m4s 文件为 m4a(AAC 音频不能直接转为 MP3 容器)
|
|
1373
1441
|
const audioPath = utils_1.Common.tempDri.video +
|
|
@@ -1551,6 +1619,64 @@ class Bilibili extends utils_1.Base {
|
|
|
1551
1619
|
}
|
|
1552
1620
|
return this.preparedVideo !== null;
|
|
1553
1621
|
}
|
|
1622
|
+
/**
|
|
1623
|
+
* 起一次互动剧情(后台长跑,不阻塞解析主流程)。
|
|
1624
|
+
*
|
|
1625
|
+
* 选项与等待都在 interactive-story 里;这里只负责:问一次剧情图版本号 → 跑剧情 → 续播时
|
|
1626
|
+
* 用**用户刚发的那条消息**重新走一遍解析链路(storyOnly:只发视频,不发卡片与评论区)。
|
|
1627
|
+
*/
|
|
1628
|
+
async startInteractiveStory() {
|
|
1629
|
+
const interactive = this.interactive;
|
|
1630
|
+
if (!interactive)
|
|
1631
|
+
return;
|
|
1632
|
+
try {
|
|
1633
|
+
const info = await (0, InteractiveVideo_1.fetchInteractiveInfo)({ bvid: interactive.bvid, cid: interactive.cid, headers: this.headers });
|
|
1634
|
+
if (!info) {
|
|
1635
|
+
node_karin_1.logger.debug('[互动视频] 播放器接口没有 interaction.graph_version,按普通视频处理');
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const result = await (0, interactive_story_1.runInteractiveStory)({
|
|
1639
|
+
e: this.e,
|
|
1640
|
+
bvid: interactive.bvid,
|
|
1641
|
+
graphVersion: info.graphVersion,
|
|
1642
|
+
rootCid: interactive.cid,
|
|
1643
|
+
title: this.workInfo?.title ?? '',
|
|
1644
|
+
notice: info.notice,
|
|
1645
|
+
headers: this.headers,
|
|
1646
|
+
onNode: async (node, event, path, isFirst, entry) => {
|
|
1647
|
+
/** 整张图第一次就爬完并缓存,之后不再判断「画没画过」:只有第一段和结局出图 */
|
|
1648
|
+
const isEnding = node.isLeaf || !node.choices.length;
|
|
1649
|
+
if (!isFirst && !isEnding)
|
|
1650
|
+
return false;
|
|
1651
|
+
/** 没勾「流程图」就只发选项面板,剧情照常能玩 */
|
|
1652
|
+
if (!Config_1.Config.bilibili.sendContent.some((item) => item === 'chart'))
|
|
1653
|
+
return false;
|
|
1654
|
+
const chart = await (0, interactive_story_1.renderInteractiveChart)({
|
|
1655
|
+
e: event,
|
|
1656
|
+
bvid: interactive.bvid,
|
|
1657
|
+
graphVersion: info.graphVersion,
|
|
1658
|
+
cid: node.cid,
|
|
1659
|
+
entry: entry ?? undefined,
|
|
1660
|
+
title: this.workInfo?.title ?? node.title ?? '互动视频',
|
|
1661
|
+
path,
|
|
1662
|
+
notice: node.notice,
|
|
1663
|
+
question: node.question,
|
|
1664
|
+
isEnding,
|
|
1665
|
+
headers: this.headers
|
|
1666
|
+
});
|
|
1667
|
+
return chart.sent;
|
|
1668
|
+
},
|
|
1669
|
+
play: async (cid, event) => {
|
|
1670
|
+
const story = new Bilibili(event, { type: 'one_video', bvid: interactive.bvid, cid }, { storyOnly: true });
|
|
1671
|
+
await story.BilibiliHandler({ type: 'one_video', bvid: interactive.bvid, cid });
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
node_karin_1.logger.info('[互动视频] 剧情结束(' + result.ended + '),共 ' + result.nodes + ' 段:' + (result.path.join(' → ') || '无'));
|
|
1675
|
+
}
|
|
1676
|
+
catch (error) {
|
|
1677
|
+
node_karin_1.logger.warn('[互动视频] 剧情流程异常结束: ' + String(error?.message ?? error));
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1554
1680
|
/**
|
|
1555
1681
|
* 把 {@link prepareVideo} 下好的视频发出去。
|
|
1556
1682
|
*
|