koishi-plugin-bilibili-videolink-analysis 0.5.0 → 0.5.2
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/lib/index.d.ts +1 -1
- package/lib/index.js +298 -132
- package/package.json +7 -5
- package/readme.md +54 -31
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apply = exports.usage = exports.Config = exports.name = exports.inject = void 0;
|
|
4
4
|
const koishi_1 = require("koishi");
|
|
5
|
-
const
|
|
5
|
+
const { Schema, Logger, h } = require("koishi");
|
|
6
|
+
const logger = new Logger('bilibili-videolink-analysis');
|
|
6
7
|
exports.name = 'bilibili-videolink-analysis';
|
|
7
|
-
exports.inject =
|
|
8
|
+
exports.inject = {
|
|
9
|
+
optional: ['puppeteer'],
|
|
10
|
+
required: ['BiliBiliVideo']
|
|
11
|
+
}
|
|
8
12
|
exports.usage = `
|
|
9
13
|
|
|
10
14
|
<h1>→ <a href="https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis" target="_blank">可以点击这里查看详细的文档说明✨</a></h1>
|
|
@@ -15,17 +19,28 @@ exports.usage = `
|
|
|
15
19
|
|
|
16
20
|
会返回视频信息与视频哦
|
|
17
21
|
|
|
18
|
-
(本插件没有注册的指令)
|
|
19
|
-
|
|
20
22
|
---
|
|
21
23
|
|
|
22
24
|
#### ⚠️ **如果你使用不了本项目,请优先检查:** ⚠️
|
|
25
|
+
#### 若无注册的指令,请关开一下[command插件](/market?keyword=commands+email:shigma10826@gmail.com)(没有指令也不影响解析别人的链接)
|
|
26
|
+
#### 视频内容是否为B站的大会员专属视频/付费视频/充电专属视频
|
|
23
27
|
#### 是否正确配置并启动了[bilibili-login插件](/market?keyword=bilibili-login) (启动即可,不是必须登录)
|
|
24
28
|
#### 接入方法是否支持获取网址链接/小程序卡片消息
|
|
25
29
|
#### 接入方法是否支持视频元素的发送
|
|
26
30
|
#### 发送视频超时/其他网络问题
|
|
27
31
|
#### 视频内容被平台屏蔽/其他平台因素
|
|
28
32
|
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 注意,点播功能需要使用 puppeteer 服务
|
|
36
|
+
|
|
37
|
+
点播功能是为了方便群友一起刷B站哦~
|
|
38
|
+
|
|
39
|
+
比如:搜索 “遠い空へ” 的第二页,并且结果以语音格式返回
|
|
40
|
+
|
|
41
|
+
示例:\`点播 遠い空へ -a -p 2\`
|
|
42
|
+
|
|
43
|
+
|
|
29
44
|
---
|
|
30
45
|
|
|
31
46
|
### 特别鸣谢 💖
|
|
@@ -39,62 +54,211 @@ exports.usage = `
|
|
|
39
54
|
|
|
40
55
|
`;
|
|
41
56
|
|
|
42
|
-
exports.Config =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
exports.Config = Schema.intersect([
|
|
58
|
+
Schema.object({
|
|
59
|
+
timeout: Schema.number().role('slider').min(1).max(300).step(1).default(60).description('指定播放视频的输入时限。`单位 秒`'),
|
|
60
|
+
point: Schema.tuple([Number, Number]).description('序号标注位置。分别表示`距离顶部 距离左侧`的百分比').default([50, 50]),
|
|
61
|
+
enable: Schema.boolean().description('是否开启自动解析`选择对应视频 会自动解析视频内容`').default(true),
|
|
62
|
+
}).description('点播设置(需要puppeteer服务)'),
|
|
63
|
+
|
|
64
|
+
Schema.object({
|
|
65
|
+
waitTip_Switch: Schema.union([
|
|
66
|
+
Schema.const().description('不返回文字提示'),
|
|
67
|
+
Schema.string().description('返回文字提示(请在右侧填写文字内容)'),
|
|
47
68
|
]).description("是否返回等待提示。开启后,会发送`等待提示语`"),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
koishi_1.Schema.const('5').description('返回视频,仅在日志记录视频直链'),
|
|
69
|
+
linktextParsing: Schema.boolean().default(true).description("是否返回 视频图文数据 `开启后,才发送视频数据的图文解析。`"),
|
|
70
|
+
VideoParsing_ToLink: Schema.union([
|
|
71
|
+
Schema.const('1').description('不返回视频/视频直链'),
|
|
72
|
+
Schema.const('2').description('仅返回视频'),
|
|
73
|
+
Schema.const('3').description('仅返回视频直链'),
|
|
74
|
+
Schema.const('4').description('返回视频和视频直链'),
|
|
75
|
+
Schema.const('5').description('返回视频,仅在日志记录视频直链'),
|
|
56
76
|
]).role('radio').default('2').description("是否返回` 视频/视频直链 `"),
|
|
57
|
-
Video_ClarityPriority:
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
Video_ClarityPriority: Schema.union([
|
|
78
|
+
Schema.const('1').description('低清晰度优先(低清晰度的视频发得快一点)'),
|
|
79
|
+
Schema.const('2').description('高清晰度优先(清晰的还是去B站看吧)'),
|
|
60
80
|
]).role('radio').default('1').description("发送的视频清晰度优先策略"),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
koishi_1.Schema.string().description('返回文字提示(请在右侧填写文字内容)').default('视频太长啦!还是去B站看吧~'),
|
|
81
|
+
BVnumberParsing: Schema.boolean().default(true).description("是否允许根据`独立的BV号`解析视频 `开启后,可以通过视频的BV号解析视频。` <br> [触发说明见README](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)"),
|
|
82
|
+
Maximumduration: Schema.number().default(25).description("允许解析的视频最大时长(分钟)`超过这个时长 就不会发视频`").min(1),
|
|
83
|
+
Maximumduration_tip: Schema.union([
|
|
84
|
+
Schema.const('不返回文字提示').description('不返回文字提示'),
|
|
85
|
+
Schema.string().description('返回文字提示(请在右侧填写文字内容)').default('视频太长啦!还是去B站看吧~'),
|
|
67
86
|
]).description("对过长视频的文字提示内容").default('视频太长啦!还是去B站看吧~'),
|
|
68
|
-
MinimumTimeInterval:
|
|
87
|
+
MinimumTimeInterval: Schema.number().default(180).description("若干`秒`内 不再处理相同链接 `防止多bot互相触发 导致的刷屏/性能浪费`").min(1),
|
|
69
88
|
}).description("基础设置"),
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
parseLimit:
|
|
73
|
-
useNumeral:
|
|
74
|
-
showError:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
koishi_1.Schema.const("av").description("AV 号"),
|
|
90
|
+
Schema.object({
|
|
91
|
+
parseLimit: Schema.number().default(3).description("单对话多链接解析上限").hidden(),
|
|
92
|
+
useNumeral: Schema.boolean().default(true).description("使用格式化数字").hidden(),
|
|
93
|
+
showError: Schema.boolean().default(false).description("当链接不正确时提醒发送者").hidden(),
|
|
94
|
+
bVideoIDPreference: Schema.union([
|
|
95
|
+
Schema.const("bv").description("BV 号"),
|
|
96
|
+
Schema.const("av").description("AV 号"),
|
|
79
97
|
]).default("bv").description("ID 偏好").hidden(),
|
|
80
|
-
bVideoImage:
|
|
81
|
-
bVideoOwner:
|
|
82
|
-
bVideoDesc:
|
|
83
|
-
bVideoStat:
|
|
84
|
-
bVideoExtraStat:
|
|
85
|
-
bVideoShowLink:
|
|
86
|
-
|
|
98
|
+
bVideoImage: Schema.boolean().default(true).description("显示封面"),
|
|
99
|
+
bVideoOwner: Schema.boolean().default(true).description("显示 UP 主"),
|
|
100
|
+
bVideoDesc: Schema.boolean().default(false).description("显示简介`有的简介真的很长`"),
|
|
101
|
+
bVideoStat: Schema.boolean().default(true).description("显示状态(*三连数据*)"),
|
|
102
|
+
bVideoExtraStat: Schema.boolean().default(true).description("显示额外状态(*弹幕&观看*)"),
|
|
103
|
+
bVideoShowLink: Schema.boolean().default(false).description("显示视频链接`开启可能会导致其他bot循环解析`"),
|
|
87
104
|
}).description("链接的图文解析设置"),
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
userAgent:
|
|
91
|
-
loggerinfo:
|
|
106
|
+
Schema.object({
|
|
107
|
+
userAgent: Schema.string().description("所有 API 请求所用的 User-Agent").default("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"),
|
|
108
|
+
loggerinfo: Schema.boolean().default(false).description("日志调试输出 `日常使用无需开启`"),
|
|
92
109
|
}).description("调试设置"),
|
|
93
110
|
]);
|
|
94
111
|
|
|
95
112
|
function apply(ctx, config) {
|
|
96
113
|
const bilibiliVideo = ctx.BiliBiliVideo;
|
|
97
114
|
ctx.middleware(async (session, next) => {
|
|
115
|
+
const links = await isProcessLinks(session, config, ctx, lastProcessedUrls, logger); // 判断是否需要解析
|
|
116
|
+
if (links) {
|
|
117
|
+
const ret = await extractLinks(session, config, ctx, lastProcessedUrls, logger); // 提取链接
|
|
118
|
+
if (ret && !isLinkProcessedRecently(ret, lastProcessedUrls, config, logger)) {
|
|
119
|
+
await processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret); // 解析视频并返回
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return next();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
ctx.command('点播 [keyword]', '点播B站视频')
|
|
126
|
+
.option('video', '-v 解析返回视频')
|
|
127
|
+
.option('audio', '-a 解析返回语音')
|
|
128
|
+
.option('link', '-l 解析返回链接')
|
|
129
|
+
.option('page', '-p <page:number> 指定页数', { fallback: '1' })
|
|
130
|
+
.example('点播 遠い空へ -v')
|
|
131
|
+
.action(async ({ options, session }, keyword) => {
|
|
132
|
+
if (!keyword) {
|
|
133
|
+
await session.execute('点播 -h')
|
|
134
|
+
return '没输入keyword'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
const url = `https://search.bilibili.com/video?keyword=${encodeURIComponent(keyword)}&page=${options.page}&o=30`
|
|
139
|
+
const page = await ctx.puppeteer.page()
|
|
140
|
+
|
|
141
|
+
await page.goto(url, {
|
|
142
|
+
waitUntil: 'networkidle2'
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
await page.addStyleTag({
|
|
146
|
+
content: `
|
|
147
|
+
div.bili-header,
|
|
148
|
+
div.login-tip,
|
|
149
|
+
div.v-popover,
|
|
150
|
+
div.right-entry__outside {
|
|
151
|
+
display: none !important;
|
|
152
|
+
}
|
|
153
|
+
`
|
|
154
|
+
})
|
|
155
|
+
// 获取视频列表并为每个视频元素添加序号
|
|
156
|
+
const videos = await page.evaluate((point) => {
|
|
157
|
+
const items = Array.from(document.querySelectorAll('.video-list-item:not([style*="display: none"])'))
|
|
158
|
+
return items.map((item, index) => {
|
|
159
|
+
const link = item.querySelector('a')
|
|
160
|
+
const href = link?.getAttribute('href') || ''
|
|
161
|
+
const idMatch = href.match(/\/video\/(BV\w+)\//)
|
|
162
|
+
const id = idMatch ? idMatch[1] : ''
|
|
163
|
+
|
|
164
|
+
if (!id) {
|
|
165
|
+
// 如果没有提取到视频ID,隐藏这个元素
|
|
166
|
+
//const htmlElement = item as HTMLElement
|
|
167
|
+
const htmlElement = item
|
|
168
|
+
htmlElement.style.display = 'none'
|
|
169
|
+
} else {
|
|
170
|
+
// 创建一个包含序号的元素,并将其插入到视频元素的正中央
|
|
171
|
+
const overlay = document.createElement('div')
|
|
172
|
+
overlay.style.position = 'absolute'
|
|
173
|
+
overlay.style.top = `${point[0]}%`
|
|
174
|
+
overlay.style.left = `${point[1]}%`
|
|
175
|
+
overlay.style.transform = 'translate(-50%, -50%)'
|
|
176
|
+
overlay.style.fontSize = '48px'
|
|
177
|
+
overlay.style.fontWeight = 'bold'
|
|
178
|
+
overlay.style.color = 'black'
|
|
179
|
+
overlay.style.zIndex = '10'
|
|
180
|
+
overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.7)' // 半透明白色背景,确保数字清晰可见
|
|
181
|
+
overlay.style.padding = '10px'
|
|
182
|
+
overlay.style.borderRadius = '8px'
|
|
183
|
+
overlay.textContent = `${index + 1}` // 序号
|
|
184
|
+
|
|
185
|
+
// 确保父元素有 `position: relative` 以正确定位
|
|
186
|
+
//const videoElement = item as HTMLElement
|
|
187
|
+
const videoElement = item
|
|
188
|
+
videoElement.style.position = 'relative'
|
|
189
|
+
videoElement.appendChild(overlay)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { id }
|
|
193
|
+
}).filter(video => video.id)
|
|
194
|
+
}, config.point) // 传递配置的 point 参数
|
|
195
|
+
|
|
196
|
+
// 如果开启了日志调试模式,打印获取到的视频信息
|
|
197
|
+
if (config.loggerinfo) {
|
|
198
|
+
ctx.logger.info(options)
|
|
199
|
+
ctx.logger.info(`共找到 ${videos.length} 个视频:`)
|
|
200
|
+
videos.forEach((video, index) => {
|
|
201
|
+
ctx.logger.info(`序号 ${index + 1}: ID - ${video.id}`)
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (videos.length === 0) {
|
|
206
|
+
await page.close()
|
|
207
|
+
return '未找到相关视频。'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 动态调整窗口大小以适应视频数量
|
|
211
|
+
const viewportHeight = 200 + videos.length * 100
|
|
212
|
+
await page.setViewport({
|
|
213
|
+
width: 1440,
|
|
214
|
+
height: viewportHeight
|
|
215
|
+
})
|
|
216
|
+
let msg;
|
|
217
|
+
// 截图
|
|
218
|
+
const videoListElement = await page.$('.video-list.row')
|
|
219
|
+
if (videoListElement) {
|
|
220
|
+
const imgBuf = await videoListElement.screenshot({
|
|
221
|
+
captureBeyondViewport: false
|
|
222
|
+
})
|
|
223
|
+
msg = h.image(imgBuf, 'image/png')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await page.close()
|
|
227
|
+
|
|
228
|
+
// 发送截图
|
|
229
|
+
await session.send(msg)
|
|
230
|
+
|
|
231
|
+
// 提示用户输入
|
|
232
|
+
await session.send(`请选择视频的序号:`)
|
|
233
|
+
|
|
234
|
+
// 等待用户输入
|
|
235
|
+
const userChoice = await session.prompt(config.timeout * 1000)
|
|
236
|
+
const choiceIndex = parseInt(userChoice) - 1
|
|
237
|
+
if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= videos.length) {
|
|
238
|
+
return '输入无效,请输入正确的序号。'
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 返回用户选择的视频ID
|
|
242
|
+
const chosenVideo = videos[choiceIndex]
|
|
243
|
+
|
|
244
|
+
// 如果开启了日志调试模式,打印用户选择的视频信息
|
|
245
|
+
if (config.loggerinfo) {
|
|
246
|
+
ctx.logger.info(`渲染序号设置\noverlay.style.top = ${config.point[0]}% \noverlay.style.left = ${config.point[1]}80%`)
|
|
247
|
+
ctx.logger.info(`用户选择了序号 ${choiceIndex + 1}: ID - ${chosenVideo.id}`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (config.enable) { // 开启自动解析了
|
|
251
|
+
session.content = `https://www.bilibili.com/video/${chosenVideo.id}`
|
|
252
|
+
const ret = await extractLinks(session, config, ctx, lastProcessedUrls, logger); // 提取链接
|
|
253
|
+
if (ret && !isLinkProcessedRecently(ret, lastProcessedUrls, config, logger)) {
|
|
254
|
+
await processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret, options); // 解析视频并返回
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
//判断是否需要解析
|
|
261
|
+
async function isProcessLinks(session, config, ctx, lastProcessedUrls, logger) {
|
|
98
262
|
let content = session.content;
|
|
99
263
|
|
|
100
264
|
// 如果允许解析 BV 号,则进行解析
|
|
@@ -108,16 +272,23 @@ function apply(ctx, config) {
|
|
|
108
272
|
// 解析内容中的链接
|
|
109
273
|
const links = link_type_parser(content);
|
|
110
274
|
if (links.length === 0) {
|
|
111
|
-
return
|
|
275
|
+
return false; // 如果没有找到链接,返回 false
|
|
112
276
|
}
|
|
113
|
-
|
|
114
|
-
|
|
277
|
+
|
|
278
|
+
return links; // 返回解析出的链接
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
//提取链接
|
|
282
|
+
async function extractLinks(session, config, ctx, lastProcessedUrls, logger) {
|
|
283
|
+
const links = link_type_parser(session.content);
|
|
284
|
+
let ret = "";
|
|
285
|
+
ret += [(0, h)("quote", { id: session.messageId })];
|
|
115
286
|
let countLink = 0;
|
|
116
287
|
let tp_ret;
|
|
288
|
+
|
|
117
289
|
// 循环检测链接类型
|
|
118
290
|
for (const element of links) {
|
|
119
|
-
if (countLink >= 1)
|
|
120
|
-
ret += "\n";
|
|
291
|
+
if (countLink >= 1) ret += "\n";
|
|
121
292
|
if (countLink >= config.parseLimit) {
|
|
122
293
|
ret += "已达到解析上限…";
|
|
123
294
|
break;
|
|
@@ -128,102 +299,102 @@ function apply(ctx, config) {
|
|
|
128
299
|
ret = "无法解析链接信息。可能是 ID 不存在,或该类型可能暂不支持。";
|
|
129
300
|
else
|
|
130
301
|
ret = null;
|
|
131
|
-
}
|
|
132
|
-
else
|
|
302
|
+
} else {
|
|
133
303
|
ret += tp_ret;
|
|
304
|
+
}
|
|
134
305
|
countLink++;
|
|
135
306
|
}
|
|
307
|
+
return ret;
|
|
308
|
+
}
|
|
136
309
|
|
|
137
|
-
|
|
138
|
-
|
|
310
|
+
//判断链接是否已经处理过
|
|
311
|
+
function isLinkProcessedRecently(ret, lastProcessedUrls, config, logger) {
|
|
312
|
+
const lastretUrl = extractLastUrl(ret); // 提取 ret 最后一个 http 链接作为解析目标
|
|
313
|
+
const currentTime = Date.now();
|
|
139
314
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (config.loggerinfo) {
|
|
144
|
-
logger.info(`重复出现,略过处理:\n ${lastretUrl}`);
|
|
145
|
-
}
|
|
146
|
-
return next();
|
|
315
|
+
if (lastProcessedUrls[lastretUrl] && (currentTime - lastProcessedUrls[lastretUrl] < config.MinimumTimeInterval * 1000)) {
|
|
316
|
+
if (config.loggerinfo) {
|
|
317
|
+
logger.info(`重复出现,略过处理:\n ${lastretUrl}`);
|
|
147
318
|
}
|
|
319
|
+
return true; // 已经处理过
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 更新该链接的最后处理时间
|
|
323
|
+
lastProcessedUrls[lastretUrl] = currentTime;
|
|
324
|
+
return false; // 没有处理过
|
|
325
|
+
}
|
|
148
326
|
|
|
149
|
-
|
|
150
|
-
|
|
327
|
+
//解析视频并返回
|
|
328
|
+
async function processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret, options) {
|
|
329
|
+
const lastretUrl = extractLastUrl(ret);
|
|
330
|
+
let bilibilimediaDataURL = '';
|
|
331
|
+
let mediaData = '';
|
|
332
|
+
|
|
333
|
+
if (config.waitTip_Switch) {
|
|
334
|
+
// 等候的提示文字
|
|
335
|
+
await session.send(config.waitTip_Switch);
|
|
336
|
+
}
|
|
151
337
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
await session.send(
|
|
338
|
+
if (config.linktextParsing) {
|
|
339
|
+
if (config.bVideoShowLink) {
|
|
340
|
+
await session.send(ret); // 发送完整信息
|
|
341
|
+
} else {
|
|
342
|
+
// 去掉最后一个链接
|
|
343
|
+
const retWithoutLastLink = ret.replace(lastretUrl, '');
|
|
344
|
+
await session.send(retWithoutLastLink);
|
|
155
345
|
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (config.VideoParsing_ToLink) {
|
|
349
|
+
const mediaDataString = JSON.stringify(await handleBilibiliMedia(bilibiliVideo, lastretUrl, config));
|
|
350
|
+
mediaData = JSON.parse(mediaDataString);
|
|
351
|
+
bilibilimediaDataURL = mediaData[0].url;
|
|
352
|
+
const videoDuration = mediaData[0].duration; // 提取视频时长,单位为秒
|
|
156
353
|
|
|
157
|
-
if (config.
|
|
158
|
-
if (config.
|
|
159
|
-
await session.send(
|
|
160
|
-
} else {
|
|
161
|
-
// 去掉最后一个链接
|
|
162
|
-
const retWithoutLastLink = ret.replace(lastretUrl, '');
|
|
163
|
-
await session.send(retWithoutLastLink);
|
|
354
|
+
if (videoDuration > config.Maximumduration * 60) {
|
|
355
|
+
if (config.Maximumduration_tip) {
|
|
356
|
+
await session.send(config.Maximumduration_tip);
|
|
164
357
|
}
|
|
358
|
+
return;
|
|
165
359
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (videoDuration > config.Maximumduration * 60) {
|
|
175
|
-
// 如果视频时长超过配置的最大值
|
|
176
|
-
if (config.Maximumduration_tip) {
|
|
177
|
-
await session.send(config.Maximumduration_tip);
|
|
178
|
-
}
|
|
179
|
-
return next();
|
|
180
|
-
}
|
|
360
|
+
if (options.text || options.link) { // 发送链接
|
|
361
|
+
await session.send(h.text(bilibilimediaDataURL));
|
|
362
|
+
return;
|
|
363
|
+
} else if (options.audio) { // 发送语音
|
|
364
|
+
await session.send(h.audio(bilibilimediaDataURL));
|
|
365
|
+
return;
|
|
366
|
+
} else { // 默认发送视频
|
|
181
367
|
// 根据配置的值来决定发送的内容
|
|
182
|
-
/*
|
|
183
|
-
* VideoParsing_ToLink: koishi_1.Schema.union([
|
|
184
|
-
*
|
|
185
|
-
* koishi_1.Schema.const('1').description('不返回视频/视频直链'),
|
|
186
|
-
* koishi_1.Schema.const('2').description('仅返回视频'),
|
|
187
|
-
* koishi_1.Schema.const('3').description('仅返回视频直链'),
|
|
188
|
-
* koishi_1.Schema.const('4').description('返回视频和视频直链'),
|
|
189
|
-
* koishi_1.Schema.const('5').description('返回视频,仅在日志记录视频直链'),
|
|
190
|
-
*
|
|
191
|
-
* ]).role('radio').default('2').description("是否返回` 视频/视频直链 `"),
|
|
192
|
-
*/
|
|
193
368
|
switch (config.VideoParsing_ToLink) {
|
|
194
|
-
case '1': // 不返回视频/视频直链
|
|
369
|
+
case '1': // 不返回视频/视频直链
|
|
195
370
|
break;
|
|
196
|
-
case '2': // 仅返回视频
|
|
197
|
-
await session.send(
|
|
371
|
+
case '2': // 仅返回视频
|
|
372
|
+
await session.send(h.video(bilibilimediaDataURL)); // 发送视频
|
|
198
373
|
break;
|
|
199
|
-
case '3': // 仅返回视频直链
|
|
200
|
-
await session.send(
|
|
374
|
+
case '3': // 仅返回视频直链
|
|
375
|
+
await session.send(h.text(bilibilimediaDataURL)); // 发送视频直链
|
|
201
376
|
break;
|
|
202
377
|
case '4': // 返回视频和视频直链
|
|
203
|
-
await session.send(
|
|
204
|
-
await session.send(
|
|
378
|
+
await session.send(h.text(bilibilimediaDataURL)); // 先发送视频直链
|
|
379
|
+
await session.send(h.video(bilibilimediaDataURL)); // 发送视频
|
|
205
380
|
break;
|
|
206
381
|
case '5': // 返回视频,记录视频链接
|
|
207
382
|
await logger.info(bilibilimediaDataURL); // 先记录日志
|
|
208
|
-
await session.send(
|
|
383
|
+
await session.send(h.video(bilibilimediaDataURL)); // 发送视频
|
|
209
384
|
break;
|
|
210
385
|
default:
|
|
211
|
-
// 处理默认情况或者错误配置
|
|
212
|
-
// 目前默认 不返回视频/视频直链
|
|
386
|
+
// 处理默认情况或者错误配置
|
|
213
387
|
break;
|
|
214
388
|
}
|
|
215
389
|
}
|
|
390
|
+
}
|
|
216
391
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
logger.info(`视频信息内容:\n ${JSON.stringify(mediaData)}`);
|
|
221
|
-
logger.info(`机器人发送完整消息为:\n ${ret}`);
|
|
222
|
-
}
|
|
223
|
-
|
|
392
|
+
if (config.loggerinfo) {
|
|
393
|
+
logger.info(`视频信息内容:\n ${JSON.stringify(mediaData)}`);
|
|
394
|
+
logger.info(`机器人发送完整消息为:\n ${ret}`);
|
|
224
395
|
}
|
|
225
|
-
return
|
|
226
|
-
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
227
398
|
|
|
228
399
|
|
|
229
400
|
// 提取最后一个URL
|
|
@@ -541,7 +712,7 @@ function apply(ctx, config) {
|
|
|
541
712
|
!pcvideoStream.data.accept_quality ||
|
|
542
713
|
!h5videoStream.data.accept_format ||
|
|
543
714
|
!pcvideoStream.data.accept_format)
|
|
544
|
-
throw new Error('无法获取清晰度信息,
|
|
715
|
+
throw new Error('无法获取清晰度信息, 可能该视频为大会员专享或者该视频为付费视频/充电专属视频!或者账号被风控。');
|
|
545
716
|
const h5Quality = h5videoStream.data.accept_quality;
|
|
546
717
|
const pcQuality = pcvideoStream.data.accept_quality;
|
|
547
718
|
if (config.loggerinfo) {
|
|
@@ -574,15 +745,15 @@ function apply(ctx, config) {
|
|
|
574
745
|
// 根据配置决定排序顺序
|
|
575
746
|
switch (config.Video_ClarityPriority) {
|
|
576
747
|
case '1':
|
|
577
|
-
|
|
578
|
-
|
|
748
|
+
//logger.info(`低清晰度优先排序,a[1]: ${a[1]}, b[1]: ${b[1]}`);
|
|
749
|
+
return a[1] - b[1]; // 从低到高排序(低清晰度优先)
|
|
579
750
|
case '2':
|
|
580
|
-
|
|
581
|
-
|
|
751
|
+
//logger.info(`高清晰度优先排序,a[1]: ${a[1]}, b[1]: ${b[1]}`);
|
|
752
|
+
return b[1] - a[1]; // 从高到低排序(高清晰度优先)
|
|
582
753
|
default:
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
754
|
+
//logger.warn(`未知的视频清晰度优先级配置: ${config.Video_ClarityPriority}`);
|
|
755
|
+
return 0; // 默认保持原顺序
|
|
756
|
+
}
|
|
586
757
|
}
|
|
587
758
|
});
|
|
588
759
|
outerLoop: for (const [index, item] of CombinedQualityInfo.entries()) {
|
|
@@ -730,13 +901,8 @@ function apply(ctx, config) {
|
|
|
730
901
|
|
|
731
902
|
mediaDataArray.push(mediaData);
|
|
732
903
|
}
|
|
733
|
-
/*
|
|
734
|
-
if (config.loggerinfo) {
|
|
735
|
-
logger.info(mediaDataArray)
|
|
736
|
-
}
|
|
737
|
-
*/
|
|
738
904
|
return mediaDataArray;
|
|
739
905
|
}
|
|
740
906
|
|
|
741
907
|
}
|
|
742
|
-
exports.apply = apply;
|
|
908
|
+
exports.apply = apply;
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-bilibili-videolink-analysis",
|
|
3
|
-
"description": "[<ruby>Bilibili视频解析<rp>(</rp><rt>点我查看食用方法</rt><rp>)</rp></ruby>](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)
|
|
3
|
+
"description": "[<ruby>Bilibili视频解析<rp>(</rp><rt>点我查看食用方法</rt><rp>)</rp></ruby>](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)解析B站链接(支持小程序卡片)支持搜索点播功能!灵感来自强大的 [iirose-media-request](/market?keyword=iirose-media-request) 以及完美的 [bili-parser](/market?keyword=bili-parser) ,十分感谢这两个优秀的项目!",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.2",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"typings": "lib/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"lib",
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
|
-
"scripts": {
|
|
12
|
+
"scripts": {
|
|
13
|
+
|
|
14
|
+
},
|
|
13
15
|
"keywords": [
|
|
14
16
|
"chatbot",
|
|
15
17
|
"koishi",
|
|
16
18
|
"plugin",
|
|
17
|
-
"B
|
|
19
|
+
"B站视频点播",
|
|
18
20
|
"Bilibili视频链接解析",
|
|
19
21
|
"bilibili-videolink-analysis"
|
|
20
22
|
],
|
|
@@ -22,4 +24,4 @@
|
|
|
22
24
|
"koishi": "^4.16.8",
|
|
23
25
|
"koishi-plugin-bilibili-login": "^0.1.50"
|
|
24
26
|
}
|
|
25
|
-
}
|
|
27
|
+
}
|
package/readme.md
CHANGED
|
@@ -24,36 +24,50 @@ email:1919892171@qq.com
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
**特别提醒**
|
|
28
|
+
|
|
29
|
+
请注意,对于B站的大会员专属视频和付费视频/充电专属视频,本插件暂不支持使用登录账号的cookie来获取视频内容。
|
|
30
|
+
|
|
31
|
+
这意味着,本插件目前无法解析或提供此类视频的观看服务。
|
|
32
|
+
|
|
33
|
+
如果您尝试解析这类视频,会收到错误提示或无法获取视频信息。
|
|
34
|
+
|
|
35
|
+
我们建议您直接在B站官网来观看这些视频。
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
27
39
|
## 📌 链接信息解析设置
|
|
28
40
|
|
|
29
41
|
### 基础设置
|
|
30
|
-
| 设置项
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
42
|
+
| 设置项 | 描述 | 默认值 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `waitTip_Switch` | 是否返回等待提示语句。开启后,会发送自定义的等待提示语。 | 不返回文字提示 |
|
|
45
|
+
| `linktextParsing` | 是否返回视频图文数据。开启后,发送视频数据的图文解析。 | `true` |
|
|
46
|
+
| `VideoParsing_ToLink` | 是否返回视频/视频直链。可以选择不同的返回策略。 | 仅返回视频 |
|
|
47
|
+
| `Video_ClarityPriority` | 发送视频时清晰度的优先策略。 | 低清晰度优先 |
|
|
48
|
+
| `BVnumberParsing` | 是否允许根据独立的BV号解析视频。开启后,可以通过视频的BV号解析视频。 | `true` |
|
|
49
|
+
| `Maximumduration` | 允许解析的视频最大时长(分钟)。超过此时长则不会发送视频。 | `25` |
|
|
50
|
+
| `Maximumduration_tip` | 对过长视频的文字提示内容。 | 视频太长啦!还是去B站看吧~ |
|
|
51
|
+
| `MinimumTimeInterval` | 若干秒内不再处理相同链接,防止多bot互相触发导致的刷屏/性能浪费。 | `180`秒 |
|
|
38
52
|
### 链接的图文解析设置
|
|
39
|
-
| 设置项
|
|
40
|
-
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
| 设置项 | 描述 | 默认值 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `parseLimit` | 单对话多链接解析上限。 | `3` |
|
|
56
|
+
| `useNumeral` | 是否使用格式化数字。 | `true` |
|
|
57
|
+
| `showError` | 当链接不正确时提醒发送者。 | `false` |
|
|
58
|
+
| `bVideoIDPreference` | ID偏好,选择视频ID类型,BV号或AV号。 | `"bv"` |
|
|
59
|
+
| `bVideoImage` | 是否显示视频封面。 | `true` |
|
|
60
|
+
| `bVideoOwner` | 是否显示视频UP主信息。 | `true` |
|
|
61
|
+
| `bVideoDesc` | 是否显示视频简介。 | `false` |
|
|
62
|
+
| `bVideoStat` | 是否显示视频状态(如三连数据)。 | `true` |
|
|
63
|
+
| `bVideoExtraStat` | 是否显示额外状态(如弹幕和观看人数)。 | `true` |
|
|
64
|
+
| `bVideoShowLink` | 是否显示视频链接。开启可能会导致其他bot循环解析。 | `false` |
|
|
65
|
+
### 调试设置
|
|
66
|
+
| 设置项 | 描述 | 默认值 |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| `userAgent` | 所有API请求所用的User-Agent。 | 默认的User-Agent字符串 |
|
|
69
|
+
| `loggerinfo` | 是否开启日志调试输出。日常使用无需开启。 | `false` |
|
|
70
|
+
---
|
|
57
71
|
|
|
58
72
|
|
|
59
73
|
## 交互示例
|
|
@@ -69,13 +83,13 @@ email:1919892171@qq.com
|
|
|
69
83
|
```
|
|
70
84
|
|
|
71
85
|
```
|
|
72
|
-
|
|
86
|
+
机器人:(如果有开启相关配置项)
|
|
73
87
|
|
|
74
88
|
等待提示的文字
|
|
75
89
|
```
|
|
76
90
|
|
|
77
91
|
```
|
|
78
|
-
机器人:
|
|
92
|
+
机器人: (如果有开启相关配置项)
|
|
79
93
|
|
|
80
94
|
UP主: 我王小桃要上一档
|
|
81
95
|
金钱焊机纯享版:BV1T142197Ai
|
|
@@ -89,7 +103,7 @@ https://www.bilibili.com/video/BV1ii421Q7oj
|
|
|
89
103
|
|
|
90
104
|
|
|
91
105
|
```
|
|
92
|
-
|
|
106
|
+
机器人:(如果有开启相关配置项)
|
|
93
107
|
|
|
94
108
|
视频内容
|
|
95
109
|
```
|
|
@@ -164,6 +178,15 @@ https://www.bilibili.com/video/BV1ii421Q7oj
|
|
|
164
178
|
<details>
|
|
165
179
|
<summary>点击此处 可查看更新日志</summary>
|
|
166
180
|
|
|
181
|
+
- **0.5.2**
|
|
182
|
+
- 支持点播功能,使用puppeteer进行网页截图,并且加以渲染序号,以便选择
|
|
183
|
+
- 模块化中间件部分内容,方便调用
|
|
184
|
+
|
|
185
|
+
- **0.5.1**
|
|
186
|
+
- 调整配置项`Maximumduration`的默认值为`25`分钟
|
|
187
|
+
- 优化对于`B站的大会员专属视频和付费视频/充电专属视频`的说明
|
|
188
|
+
- 优化对`无法获取清晰度信息`情况的报错的问题描述
|
|
189
|
+
|
|
167
190
|
- **0.5.0**
|
|
168
191
|
- 新增配置项`Maximumduration`和`Maximumduration_tip`,允许用户配置可以解析的最长的视频时长
|
|
169
192
|
- 新增配置项`Video_ClarityPriority`,允许用户配置视频清晰度发送策略
|
|
@@ -176,8 +199,8 @@ https://www.bilibili.com/video/BV1ii421Q7oj
|
|
|
176
199
|
- 优化调试模式的日志输出
|
|
177
200
|
|
|
178
201
|
- **0.4.9** 优化控制台超链接
|
|
179
|
-
|
|
180
|
-
|
|
202
|
+
- 让插件超链接仅在本窗口打开,详细说明的超链接在新窗口打开
|
|
203
|
+
- `hidden()`掉部分原先插件的配置项
|
|
181
204
|
|
|
182
205
|
- **0.4.8**
|
|
183
206
|
- 优化插件说明
|