@xzxzzx/bilibili-mcp 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 365903728-oss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # Bilibili MCP Tool
2
+ 用claude code(glm4,7模型)做的提取B站视频的MCP
3
+ Bilibili MCP (Model Context Protocol) 工具,用于总结 Bilibili 视频和视频评论。
4
+
5
+ ## 功能特性
6
+
7
+ ### 1. 视频总结 (`get_video_info`)
8
+ - 优先获取视频的 CC 或 AI 字幕
9
+ - 无字幕时自动降级为视频标题、简介和标签
10
+ - 支持多语言字幕选择(默认优先简体中文)
11
+ - 可手动指定偏好字幕语言
12
+
13
+ ### 2. 评论总结 (`get_video_comments`)
14
+ - 获取视频热门评论
15
+ - 自动过滤表情占位符(如 `[doge]`)
16
+ - 优先保留包含时间戳的评论(如 `05:20`)
17
+ - 支持两种详细程度:
18
+ - `brief`: 10 条热门评论
19
+ - `detailed`: 50 条热门评论 + 高赞回复
20
+
21
+ ## 安装
22
+
23
+ ```bash
24
+ # 安装依赖
25
+ npm install
26
+
27
+ # 构建
28
+ npm run build
29
+ ```
30
+
31
+ ## 使用
32
+
33
+ ### 方式一:作为 MCP 服务器
34
+
35
+ 在 Claude Desktop 的配置文件中添加:
36
+
37
+ **macOS/Linux**: `~/Library/Application Support/Claude/claude_desktop_config.json`
38
+
39
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "bilibili": {
45
+ "command": "node",
46
+ "args": ["C:\\Users\\ZX\\bilibili-mcp\\dist\\index.js"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### 方式二:直接运行
53
+
54
+ ```bash
55
+ npm start
56
+ ```
57
+
58
+ ## 工具使用示例
59
+
60
+ ### 获取视频信息(含字幕)
61
+
62
+ ```json
63
+ {
64
+ "name": "get_video_info",
65
+ "arguments": {
66
+ "bvid_or_url": "BV1xx4x1x7xx"
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### 获取视频信息(指定语言)
72
+
73
+ ```json
74
+ {
75
+ "name": "get_video_info",
76
+ "arguments": {
77
+ "bvid_or_url": "BV1xx4x1x7xx",
78
+ "preferred_lang": "en"
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### 获取评论(简略模式)
84
+
85
+ ```json
86
+ {
87
+ "name": "get_video_comments",
88
+ "arguments": {
89
+ "bvid_or_url": "BV1xx4x1x7xx",
90
+ "detail_level": "brief"
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### 获取评论(详细模式)
96
+
97
+ ```json
98
+ {
99
+ "name": "get_video_comments",
100
+ "arguments": {
101
+ "bvid_or_url": "BV1xx4x1x7xx",
102
+ "detail_level": "detailed"
103
+ }
104
+ }
105
+ ```
106
+
107
+ ## 返回数据格式
108
+
109
+ ### 视频信息返回格式
110
+
111
+ ```json
112
+ {
113
+ "data_source": "subtitle",
114
+ "video_info": {
115
+ "title": "视频标题",
116
+ "description": "视频简介",
117
+ "tags": ["标签1", "标签2"],
118
+ "subtitle_text": "字幕内容..."
119
+ }
120
+ }
121
+ ```
122
+
123
+ 当 `data_source` 为 `description` 时,表示没有字幕,只有基本信息。
124
+
125
+ ### 评论返回格式
126
+
127
+ ```json
128
+ {
129
+ "comments": [
130
+ {
131
+ "author": "用户名",
132
+ "content": "评论内容",
133
+ "likes": 123,
134
+ "has_timestamp": true,
135
+ "timestamp": "05:20"
136
+ }
137
+ ],
138
+ "summary": {
139
+ "total_comments": 50,
140
+ "comments_with_timestamp": 5
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## 安全性
146
+
147
+ - 仅使用 Bilibili 公开 API,无需登录
148
+ - 不存储任何用户数据
149
+ - 代码开源可审计
150
+ - 错误输出使用 `console.error` 避免干扰 Stdio 协议
151
+
152
+ ## API 限流机制
153
+
154
+ 为了遵守 Bilibili 的 API 使用规范并防止被限制,本工具实现了请求限流机制:
155
+
156
+ ### 限流配置
157
+
158
+ | 配置项 | 值 |
159
+ |--------|-----|
160
+ | 请求间隔 | 500ms(0.5 秒) |
161
+ | 请求方式 | 队列顺序执行,不并发 |
162
+
163
+ ### 为什么需要限流
164
+
165
+ 1. **遵守 Bilibili API 规范** - 避免高频请求导致账号或 IP 被限制
166
+ 2. **保证稳定性** - 降低 API 返回错误或被拒绝的风险
167
+ 3. **尊重服务提供方** - 合理使用公共资源
168
+
169
+ ### 对用户的影响
170
+
171
+ | 操作 | 预计额外延迟 |
172
+ |------|-------------|
173
+ | 获取视频信息 | 约 1-1.5 秒(2-3 个 API 调用) |
174
+ | 获取视频评论 | 约 1 秒(2 个 API 调用) |
175
+
176
+ 对于视频总结的使用场景,这个延迟是**可接受**的,因为:
177
+ - 用户通常一次只总结一个视频
178
+ - 1-2 秒的等待时间在正常范围内
179
+ - 避免被限流后完全无法使用的风险
180
+
181
+ ## 免责声明
182
+
183
+ 本项目仅供学习和个人使用,请确保您遵守 Bilibili 的服务条款和相关法律法规。
184
+
185
+ **使用须知:**
186
+
187
+ 1. **仅限非商业用途** - 本工具不应用于商业目的或商业分发
188
+ 2. **尊重版权** - 视频字幕、总结等内容可能受版权保护,请勿擅自传播或用于商业用途
189
+ 3. **遵守服务条款** - 使用本工具即表示您同意遵守 Bilibili 的用户协议和 API 使用规范
190
+ 4. **数据隐私** - 本工具不存储、收集或传输任何用户的私密信息
191
+ 5. **风险自负** - 作者不对因使用本工具导致的任何问题或损失负责
192
+ 6. **API 变更** - Bilibili 可能随时修改或关闭其 API,本工具可能因此无法正常工作
193
+ 7. **请求限制** - 请勿高频请求,以免被 Bilibili 限制或封禁
194
+
195
+ **注意:** 本项目与 Bilibili 官方无关,并非 Bilibili 官方产品。Bilibili 是哔哩哔哩弹幕网的商标。
196
+
197
+ ## 开发
198
+
199
+ ```bash
200
+ # 监听模式编译
201
+ npm run watch
202
+ ```
203
+
204
+ ## 许可证
205
+
206
+ MIT
@@ -0,0 +1,94 @@
1
+ /**
2
+ * 带有 WBI 签名的 GET 请求
3
+ */
4
+ export declare function fetchWithWBI(path: string, params: Record<string, string | number>): Promise<unknown>;
5
+ /**
6
+ * 普通的 GET 请求(不需要 WBI 签名)
7
+ */
8
+ export declare function fetchWithoutWBI(path: string, params?: Record<string, string | number>): Promise<unknown>;
9
+ /**
10
+ * 获取视频基本信息
11
+ */
12
+ export declare function getVideoInfo(bvid: string): Promise<{
13
+ title: string;
14
+ desc: string;
15
+ pic?: string;
16
+ owner: {
17
+ name: string;
18
+ face: string;
19
+ };
20
+ stat: {
21
+ view: number;
22
+ danmaku: number;
23
+ reply: number;
24
+ favorite: number;
25
+ coin: number;
26
+ share: number;
27
+ like: number;
28
+ };
29
+ cid: number;
30
+ duration: number;
31
+ pubdate: number;
32
+ tag?: {
33
+ tag_name: string;
34
+ }[];
35
+ }>;
36
+ /**
37
+ * 获取视频字幕信息
38
+ */
39
+ export declare function getVideoSubtitle(bvid: string, cid: number): Promise<{
40
+ subtitle: {
41
+ subtitles: Array<{
42
+ id: number;
43
+ lan: string;
44
+ lan_doc: string;
45
+ subtitle_url: string;
46
+ }>;
47
+ };
48
+ }>;
49
+ /**
50
+ * 获取字幕内容
51
+ */
52
+ export declare function getSubtitleContent(url: string): Promise<{
53
+ body: Array<{
54
+ from: number;
55
+ to: number;
56
+ location: number;
57
+ content: string;
58
+ }>;
59
+ }>;
60
+ /**
61
+ * 获取视频评论
62
+ */
63
+ export declare function getVideoComments(oid: number, page?: number, pageSize?: number): Promise<{
64
+ replies: Array<{
65
+ rpid: number;
66
+ member: {
67
+ uname: string;
68
+ avatar: string;
69
+ };
70
+ content: {
71
+ message: string;
72
+ };
73
+ like: number;
74
+ reply_control: {
75
+ sub_reply_entry_text?: string;
76
+ show_status?: number;
77
+ };
78
+ replies?: Array<{
79
+ member: {
80
+ uname: string;
81
+ avatar: string;
82
+ };
83
+ content: {
84
+ message: string;
85
+ };
86
+ like: number;
87
+ }>;
88
+ }>;
89
+ page: {
90
+ num: number;
91
+ size: number;
92
+ };
93
+ }>;
94
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/bilibili/client.ts"],"names":[],"mappings":"AAiKA;;GAEG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GACtC,OAAO,CAAC,OAAO,CAAC,CAyClB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GACvC,OAAO,CAAC,OAAO,CAAC,CAkClB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM;WAEpC,MAAM;UACP,MAAM;UACN,MAAM;WACL;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;UAC/B;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;SAC9G,MAAM;cACD,MAAM;aACP,MAAM;UACT;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE;GAE/B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;cAElD;QACR,SAAS,EAAE,KAAK,CAAC;YACf,EAAE,EAAE,MAAM,CAAC;YACX,GAAG,EAAE,MAAM,CAAC;YACZ,OAAO,EAAE,MAAM,CAAC;YAChB,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC,CAAC;KACJ;GAEJ;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,IAAI,EAAE,KAAK,CAAC;QACV,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ,CAAC,CAuBD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,MAAU,EAChB,QAAQ,GAAE,MAAW;aAQV,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,OAAO,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAC7B,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE;YAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvE,OAAO,CAAC,EAAE,KAAK,CAAC;YACd,MAAM,EAAE;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC;YAC1C,OAAO,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAA;aAAE,CAAC;YAC7B,IAAI,EAAE,MAAM,CAAC;SACd,CAAC,CAAC;KACJ,CAAC;UACI;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;GAEtC"}
@@ -0,0 +1,258 @@
1
+ // B站 API 客户端,包含 WBI 签名逻辑
2
+ const BASE_URL = "https://api.bilibili.com";
3
+ // WBI 缓存
4
+ let cachedWBI = null;
5
+ // 请求限流 - 避免高频请求被 Bilibili 限制
6
+ const RATE_LIMIT_MS = 500; // 请求间隔 500ms
7
+ let lastRequestTime = 0;
8
+ let pendingPromise = null;
9
+ /**
10
+ * 等待到下一个允许请求的时间
11
+ */
12
+ async function waitForRateLimit() {
13
+ const now = Date.now();
14
+ const timeSinceLastRequest = now - lastRequestTime;
15
+ if (timeSinceLastRequest < RATE_LIMIT_MS) {
16
+ const waitTime = RATE_LIMIT_MS - timeSinceLastRequest;
17
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
18
+ }
19
+ lastRequestTime = Date.now();
20
+ }
21
+ /**
22
+ * 带限流的请求包装器
23
+ */
24
+ async function throttledFetch(fetchFn) {
25
+ // 等待上一个请求完成
26
+ if (pendingPromise) {
27
+ await pendingPromise;
28
+ }
29
+ // 创建新的请求
30
+ pendingPromise = (async () => {
31
+ await waitForRateLimit();
32
+ })();
33
+ try {
34
+ await pendingPromise;
35
+ return await fetchFn();
36
+ }
37
+ finally {
38
+ pendingPromise = null;
39
+ }
40
+ }
41
+ /**
42
+ * 生成 WBI 签名所需的混合密钥
43
+ */
44
+ function getMixKey(imgKey, subKey) {
45
+ // WBI 签名使用特定的混合顺序
46
+ const saltTable = [
47
+ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
48
+ 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
49
+ 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
50
+ 36, 20, 34, 44, 52
51
+ ];
52
+ const mixKey = imgKey + subKey;
53
+ return saltTable.map((i) => mixKey[i]).join("");
54
+ }
55
+ /**
56
+ * 获取 WBI 签名密钥
57
+ */
58
+ async function getWBI() {
59
+ // 检查缓存是否有效(1小时过期)
60
+ const now = Date.now();
61
+ if (cachedWBI && cachedWBI.expireTime > now) {
62
+ return { imgKey: cachedWBI.imgKey, subKey: cachedWBI.subKey, mixKey: cachedWBI.mixKey };
63
+ }
64
+ try {
65
+ // 获取 nav 数据中的 wbi_img 字段
66
+ const navRes = await fetch(`${BASE_URL}/x/web-interface/nav`, {
67
+ headers: {
68
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
69
+ "Referer": "https://www.bilibili.com",
70
+ },
71
+ });
72
+ if (!navRes.ok) {
73
+ throw new Error(`Failed to fetch WBI: ${navRes.status}`);
74
+ }
75
+ const navData = await navRes.json();
76
+ const wbiImg = navData.data?.wbi_img;
77
+ if (!wbiImg) {
78
+ throw new Error("WBI image data not found");
79
+ }
80
+ // 提取 img_key 和 sub_key
81
+ // 格式类似: img_url: https://i0.hdslb.com/bfs/wbi/2608f8a68f3141d9_2.jpg
82
+ const imgKeyMatch = wbiImg.img_url?.match(/([^\/_]+)(?=\.jpg)/);
83
+ const subKeyMatch = wbiImg.sub_url?.match(/([^\/_]+)(?=\.jpg)/);
84
+ if (!imgKeyMatch || !subKeyMatch) {
85
+ throw new Error("Failed to extract WBI keys");
86
+ }
87
+ const imgKey = imgKeyMatch[0];
88
+ const subKey = subKeyMatch[0];
89
+ const mixKey = getMixKey(imgKey, subKey);
90
+ // 缓存 WBI(1小时后过期)
91
+ cachedWBI = {
92
+ imgKey,
93
+ subKey,
94
+ mixKey,
95
+ expireTime: now + 60 * 60 * 1000,
96
+ };
97
+ return { imgKey, subKey, mixKey };
98
+ }
99
+ catch (error) {
100
+ console.error("Error getting WBI:", error);
101
+ throw error;
102
+ }
103
+ }
104
+ /**
105
+ * 生成 WBI 签名
106
+ */
107
+ function generateWBISign(params, mixKey) {
108
+ // 将参数按字典序排序
109
+ const sortedParams = Object.keys(params)
110
+ .sort()
111
+ .reduce((result, key) => {
112
+ result[key] = params[key];
113
+ return result;
114
+ }, {});
115
+ // 构建 query 字符串
116
+ const queryStr = Object.entries(sortedParams)
117
+ .map(([key, value]) => `${key}=${value}`)
118
+ .join("&");
119
+ // 计算 w_rid
120
+ const strToSign = queryStr + mixKey;
121
+ const w_rid = simpleHash(strToSign);
122
+ return w_rid;
123
+ }
124
+ /**
125
+ * 简单哈希函数(模拟 B站的哈希算法)
126
+ */
127
+ function simpleHash(str) {
128
+ // 使用 Web Crypto API 的 MD5 或简单的自定义哈希
129
+ // 这里使用一个简化的实现
130
+ let hash = 0;
131
+ for (let i = 0; i < str.length; i++) {
132
+ const char = str.charCodeAt(i);
133
+ hash = (hash << 5) - hash + char;
134
+ hash = hash & hash; // Convert to 32bit integer
135
+ }
136
+ return Math.abs(hash).toString(16).padStart(8, "0");
137
+ }
138
+ /**
139
+ * 带有 WBI 签名的 GET 请求
140
+ */
141
+ export async function fetchWithWBI(path, params) {
142
+ return throttledFetch(async () => {
143
+ try {
144
+ const { mixKey } = await getWBI();
145
+ // 添加时间戳参数
146
+ params = { ...params, timestamp: Date.now() };
147
+ // 生成签名
148
+ const w_rid = generateWBISign(params, mixKey);
149
+ // 构建 URL
150
+ const url = new URL(path, BASE_URL);
151
+ Object.entries({ ...params, w_rid }).forEach(([key, value]) => {
152
+ url.searchParams.append(key, String(value));
153
+ });
154
+ const response = await fetch(url.toString(), {
155
+ headers: {
156
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
157
+ "Referer": "https://www.bilibili.com",
158
+ "Accept": "application/json",
159
+ },
160
+ });
161
+ if (!response.ok) {
162
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
163
+ }
164
+ const data = await response.json();
165
+ if (data.code !== 0) {
166
+ throw new Error(`API Error: ${data.message || "Unknown error"}`);
167
+ }
168
+ return data.data;
169
+ }
170
+ catch (error) {
171
+ console.error(`Error fetching ${path}:`, error);
172
+ throw error;
173
+ }
174
+ });
175
+ }
176
+ /**
177
+ * 普通的 GET 请求(不需要 WBI 签名)
178
+ */
179
+ export async function fetchWithoutWBI(path, params) {
180
+ return throttledFetch(async () => {
181
+ try {
182
+ const url = new URL(path, BASE_URL);
183
+ if (params) {
184
+ Object.entries(params).forEach(([key, value]) => {
185
+ url.searchParams.append(key, String(value));
186
+ });
187
+ }
188
+ const response = await fetch(url.toString(), {
189
+ headers: {
190
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
191
+ "Referer": "https://www.bilibili.com",
192
+ "Accept": "application/json",
193
+ },
194
+ });
195
+ if (!response.ok) {
196
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
197
+ }
198
+ const data = await response.json();
199
+ if (data.code !== 0) {
200
+ throw new Error(`API Error: ${data.message || "Unknown error"}`);
201
+ }
202
+ return data.data;
203
+ }
204
+ catch (error) {
205
+ console.error(`Error fetching ${path}:`, error);
206
+ throw error;
207
+ }
208
+ });
209
+ }
210
+ /**
211
+ * 获取视频基本信息
212
+ */
213
+ export async function getVideoInfo(bvid) {
214
+ return fetchWithoutWBI("/x/web-interface/view", { bvid });
215
+ }
216
+ /**
217
+ * 获取视频字幕信息
218
+ */
219
+ export async function getVideoSubtitle(bvid, cid) {
220
+ return fetchWithWBI("/x/player/wbi/v2", { bvid, cid });
221
+ }
222
+ /**
223
+ * 获取字幕内容
224
+ */
225
+ export async function getSubtitleContent(url) {
226
+ return throttledFetch(async () => {
227
+ try {
228
+ // 字幕 URL 可能是相对路径,需要补全
229
+ const fullUrl = url.startsWith("http") ? url : `https:${url}`;
230
+ const response = await fetch(fullUrl, {
231
+ headers: {
232
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
233
+ "Referer": "https://www.bilibili.com",
234
+ },
235
+ });
236
+ if (!response.ok) {
237
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
238
+ }
239
+ return await response.json();
240
+ }
241
+ catch (error) {
242
+ console.error("Error fetching subtitle content:", error);
243
+ throw error;
244
+ }
245
+ });
246
+ }
247
+ /**
248
+ * 获取视频评论
249
+ */
250
+ export async function getVideoComments(oid, page = 1, pageSize = 20) {
251
+ return fetchWithoutWBI("/x/v2/reply", {
252
+ oid,
253
+ type: "1",
254
+ mode: "3", // 3表示按热度排序
255
+ pagination_str: JSON.stringify({ offset: "" }),
256
+ });
257
+ }
258
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bilibili/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface CommentData {
2
+ comments: Array<{
3
+ author: string;
4
+ content: string;
5
+ likes: number;
6
+ has_timestamp: boolean;
7
+ timestamp?: string;
8
+ }>;
9
+ summary: {
10
+ total_comments: number;
11
+ comments_with_timestamp: number;
12
+ };
13
+ }
14
+ /**
15
+ * 获取视频评论
16
+ */
17
+ export declare function getVideoCommentsData(bvidOrUrl: string, detailLevel?: "brief" | "detailed"): Promise<CommentData>;
18
+ //# sourceMappingURL=comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/bilibili/comments.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,OAAO,EAAE;QACP,cAAc,EAAE,MAAM,CAAC;QACvB,uBAAuB,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAiED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,WAAW,GAAE,OAAO,GAAG,UAAoB,GAC1C,OAAO,CAAC,WAAW,CAAC,CAsDtB"}
@@ -0,0 +1,102 @@
1
+ // 评论处理逻辑
2
+ import { getVideoInfo, getVideoComments } from "./client.js";
3
+ /**
4
+ * 从 BV 号或 URL 中提取 BV 号
5
+ */
6
+ function extractBVId(input) {
7
+ const match = input.match(/(BV[a-zA-Z0-9]{10})/);
8
+ if (!match) {
9
+ throw new Error("Invalid Bilibili video ID or URL");
10
+ }
11
+ return match[1];
12
+ }
13
+ /**
14
+ * 过滤表情占位符(如 [doge])
15
+ */
16
+ function filterEmojis(text) {
17
+ // 移除所有中括号包裹的表情占位符
18
+ return text.replace(/\[[a-zA-Z0-9_]+\]/g, "").trim();
19
+ }
20
+ /**
21
+ * 检测评论中是否包含时间戳
22
+ */
23
+ function extractTimestamp(text) {
24
+ // 匹配时间戳格式,如 05:20, 1:23:45, 00:10 等
25
+ const timestampRegex = /\b(\d{1,2}:)?\d{1,2}:\d{2}\b/g;
26
+ const matches = text.match(timestampRegex);
27
+ if (matches) {
28
+ // 返回第一个匹配的时间戳
29
+ return matches[0];
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * 处理单条评论
35
+ */
36
+ function processComment(comment, includeReplies = false) {
37
+ const author = comment.member?.uname || "匿名用户";
38
+ const rawContent = comment.content?.message || "";
39
+ const filteredContent = filterEmojis(rawContent);
40
+ const likes = comment.like || 0;
41
+ const timestamp = extractTimestamp(filteredContent);
42
+ return {
43
+ author,
44
+ content: filteredContent,
45
+ likes,
46
+ has_timestamp: !!timestamp,
47
+ timestamp: timestamp || undefined,
48
+ };
49
+ }
50
+ /**
51
+ * 获取视频评论
52
+ */
53
+ export async function getVideoCommentsData(bvidOrUrl, detailLevel = "brief") {
54
+ try {
55
+ const bvid = extractBVId(bvidOrUrl);
56
+ // 获取视频基本信息以获取 CID
57
+ const videoData = await getVideoInfo(bvid);
58
+ const cid = videoData.cid;
59
+ // 根据详情级别确定评论数量
60
+ const commentCount = detailLevel === "brief" ? 10 : 50;
61
+ // 获取评论
62
+ const commentsData = await getVideoComments(cid, 1, commentCount);
63
+ const rawComments = commentsData?.replies || [];
64
+ // 处理评论
65
+ let processedComments = rawComments.map((comment) => processComment(comment));
66
+ // 如果是详细模式,添加高赞回复
67
+ if (detailLevel === "detailed") {
68
+ const replies = [];
69
+ for (const comment of rawComments) {
70
+ if (comment.replies && comment.replies.length > 0) {
71
+ // 取前3条高赞回复
72
+ const topReplies = comment.replies.slice(0, 3);
73
+ replies.push(...topReplies);
74
+ }
75
+ }
76
+ const processedReplies = replies.map((reply) => processComment(reply));
77
+ processedComments.push(...processedReplies);
78
+ }
79
+ // 优先排序:有时间戳的评论排在前面
80
+ processedComments.sort((a, b) => {
81
+ if (a.has_timestamp && !b.has_timestamp)
82
+ return -1;
83
+ if (!a.has_timestamp && b.has_timestamp)
84
+ return 1;
85
+ return b.likes - a.likes; // 都有或都没有时间戳,按点赞数排序
86
+ });
87
+ // 统计
88
+ const commentsWithTimestamp = processedComments.filter((c) => c.has_timestamp).length;
89
+ return {
90
+ comments: processedComments,
91
+ summary: {
92
+ total_comments: processedComments.length,
93
+ comments_with_timestamp: commentsWithTimestamp,
94
+ },
95
+ };
96
+ }
97
+ catch (error) {
98
+ console.error("Error getting video comments:", error);
99
+ throw error;
100
+ }
101
+ }
102
+ //# sourceMappingURL=comments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/bilibili/comments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface SubtitleData {
2
+ data_source: "subtitle" | "description";
3
+ video_info: {
4
+ title: string;
5
+ description: string;
6
+ tags: string[];
7
+ subtitle_text?: string;
8
+ };
9
+ }
10
+ /**
11
+ * 获取视频信息及字幕
12
+ */
13
+ export declare function getVideoInfoWithSubtitle(bvidOrUrl: string, preferredLang?: string): Promise<SubtitleData>;
14
+ //# sourceMappingURL=subtitle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtitle.d.ts","sourceRoot":"","sources":["../../src/bilibili/subtitle.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,UAAU,GAAG,aAAa,CAAC;IACxC,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAmED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,YAAY,CAAC,CAqFvB"}
@@ -0,0 +1,136 @@
1
+ // 字幕处理逻辑
2
+ import { getVideoInfo, getVideoSubtitle, getSubtitleContent } from "./client.js";
3
+ /**
4
+ * 字幕语言优先级
5
+ */
6
+ const LANGUAGE_PRIORITY = ["zh-Hans", "zh-CN", "zh-Hant", "en"];
7
+ /**
8
+ * 从 BV 号或 URL 中提取 BV 号
9
+ */
10
+ function extractBVId(input) {
11
+ // 匹配 BV 号格式:BV1xx4x1x7xx 或类似格式
12
+ const match = input.match(/(BV[a-zA-Z0-9]{10})/);
13
+ if (!match) {
14
+ throw new Error("Invalid Bilibili video ID or URL");
15
+ }
16
+ return match[1];
17
+ }
18
+ /**
19
+ * 选择最佳字幕语言
20
+ */
21
+ function selectBestSubtitle(subtitles, preferredLang) {
22
+ if (!subtitles || subtitles.length === 0) {
23
+ return null;
24
+ }
25
+ // 如果用户指定了偏好语言,优先使用
26
+ if (preferredLang) {
27
+ const preferred = subtitles.find((s) => s.lan === preferredLang || s.lan_doc.includes(preferredLang));
28
+ if (preferred) {
29
+ return preferred;
30
+ }
31
+ }
32
+ // 按优先级选择
33
+ for (const lang of LANGUAGE_PRIORITY) {
34
+ const subtitle = subtitles.find((s) => s.lan === lang || s.lan.includes(lang));
35
+ if (subtitle) {
36
+ return subtitle;
37
+ }
38
+ }
39
+ // 如果没有匹配的语言,返回第一个
40
+ return subtitles[0];
41
+ }
42
+ /**
43
+ * 合并字幕内容为文本
44
+ */
45
+ function mergeSubtitleText(body) {
46
+ return body.map((item) => item.content).join("\n");
47
+ }
48
+ /**
49
+ * 提取视频标签
50
+ */
51
+ function extractTags(videoData) {
52
+ const tags = videoData.tag || [];
53
+ return tags.map((tag) => tag.tag_name);
54
+ }
55
+ /**
56
+ * 获取视频信息及字幕
57
+ */
58
+ export async function getVideoInfoWithSubtitle(bvidOrUrl, preferredLang) {
59
+ try {
60
+ const bvid = extractBVId(bvidOrUrl);
61
+ // 获取视频基本信息
62
+ const videoData = await getVideoInfo(bvid);
63
+ const title = videoData.title;
64
+ const description = videoData.desc;
65
+ const tags = extractTags(videoData);
66
+ const cid = videoData.cid;
67
+ // 尝试获取字幕
68
+ try {
69
+ const subtitleData = await getVideoSubtitle(bvid, cid);
70
+ if (!subtitleData?.subtitle?.subtitles || subtitleData.subtitle.subtitles.length === 0) {
71
+ // 没有字幕,使用简介作为降级方案
72
+ console.error(`No subtitles available for video ${bvid}`);
73
+ return {
74
+ data_source: "description",
75
+ video_info: {
76
+ title,
77
+ description,
78
+ tags,
79
+ },
80
+ };
81
+ }
82
+ // 选择最佳字幕
83
+ const bestSubtitle = selectBestSubtitle(subtitleData.subtitle.subtitles, preferredLang);
84
+ if (!bestSubtitle) {
85
+ return {
86
+ data_source: "description",
87
+ video_info: {
88
+ title,
89
+ description,
90
+ tags,
91
+ },
92
+ };
93
+ }
94
+ // 获取字幕内容
95
+ const subtitleContent = await getSubtitleContent(bestSubtitle.subtitle_url);
96
+ if (!subtitleContent?.body || subtitleContent.body.length === 0) {
97
+ return {
98
+ data_source: "description",
99
+ video_info: {
100
+ title,
101
+ description,
102
+ tags,
103
+ },
104
+ };
105
+ }
106
+ // 合并字幕文本
107
+ const subtitleText = mergeSubtitleText(subtitleContent.body);
108
+ return {
109
+ data_source: "subtitle",
110
+ video_info: {
111
+ title,
112
+ description,
113
+ tags,
114
+ subtitle_text: subtitleText,
115
+ },
116
+ };
117
+ }
118
+ catch (error) {
119
+ // 获取字幕失败,使用简介作为降级方案
120
+ console.error(`Failed to fetch subtitles for video ${bvid}, using description as fallback:`, error);
121
+ return {
122
+ data_source: "description",
123
+ video_info: {
124
+ title,
125
+ description,
126
+ tags,
127
+ },
128
+ };
129
+ }
130
+ }
131
+ catch (error) {
132
+ console.error("Error getting video info with subtitle:", error);
133
+ throw error;
134
+ }
135
+ }
136
+ //# sourceMappingURL=subtitle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtitle.js","sourceRoot":"","sources":["../../src/bilibili/subtitle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ // MCP 服务器入口
2
+ import { server } from "./server.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ // 启动服务器
5
+ async function main() {
6
+ const transport = new StdioServerTransport();
7
+ await server.connect(transport);
8
+ console.error("Bilibili MCP server running on stdio");
9
+ }
10
+ main().catch((error) => {
11
+ console.error("Fatal error in main():", error);
12
+ process.exit(1);
13
+ });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,QAAQ;AACR,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;AACxD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export declare const server: Server<{
3
+ method: string;
4
+ params?: {
5
+ [x: string]: unknown;
6
+ _meta?: {
7
+ [x: string]: unknown;
8
+ progressToken?: string | number | undefined;
9
+ "io.modelcontextprotocol/related-task"?: {
10
+ taskId: string;
11
+ } | undefined;
12
+ } | undefined;
13
+ } | undefined;
14
+ }, {
15
+ method: string;
16
+ params?: {
17
+ [x: string]: unknown;
18
+ _meta?: {
19
+ [x: string]: unknown;
20
+ progressToken?: string | number | undefined;
21
+ "io.modelcontextprotocol/related-task"?: {
22
+ taskId: string;
23
+ } | undefined;
24
+ } | undefined;
25
+ } | undefined;
26
+ }, {
27
+ [x: string]: unknown;
28
+ _meta?: {
29
+ [x: string]: unknown;
30
+ progressToken?: string | number | undefined;
31
+ "io.modelcontextprotocol/related-task"?: {
32
+ taskId: string;
33
+ } | undefined;
34
+ } | undefined;
35
+ }>;
36
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAUnE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUlB,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,116 @@
1
+ // MCP 服务器定义
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ import { getVideoInfoWithSubtitle } from "./bilibili/subtitle.js";
5
+ import { getVideoCommentsData } from "./bilibili/comments.js";
6
+ // 创建 MCP 服务器实例
7
+ export const server = new Server({
8
+ name: "bilibili-mcp-server",
9
+ version: "1.0.0",
10
+ }, {
11
+ capabilities: {
12
+ tools: {},
13
+ },
14
+ });
15
+ // 注册工具列表处理器
16
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
17
+ return {
18
+ tools: [
19
+ {
20
+ name: "get_video_info",
21
+ description: "获取 Bilibili 视频信息,优先返回字幕内容,如无字幕则返回视频简介和标签。支持指定偏好语言。",
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ bvid_or_url: {
26
+ type: "string",
27
+ description: "Bilibili 视频 BV 号或完整 URL",
28
+ },
29
+ preferred_lang: {
30
+ type: "string",
31
+ description: "可选参数,指定偏好字幕语言代码,如 'zh-Hans', 'zh-Hant', 'en' 等。默认按 zh-Hans -> zh-Hant -> en 顺序选择。",
32
+ },
33
+ },
34
+ required: ["bvid_or_url"],
35
+ },
36
+ },
37
+ {
38
+ name: "get_video_comments",
39
+ description: "获取 Bilibili 视频热门评论。过滤表情占位符,优先保留包含时间戳的评论(如 '05:20')。支持 brief(10条)和 detailed(50条+回复)两种模式。",
40
+ inputSchema: {
41
+ type: "object",
42
+ properties: {
43
+ bvid_or_url: {
44
+ type: "string",
45
+ description: "Bilibili 视频 BV 号或完整 URL",
46
+ },
47
+ detail_level: {
48
+ type: "string",
49
+ description: "评论详细程度:'brief' 获取前10条热门评论;'detailed' 获取前50条热门评论及其高赞回复",
50
+ enum: ["brief", "detailed"],
51
+ },
52
+ },
53
+ required: ["bvid_or_url"],
54
+ },
55
+ },
56
+ ],
57
+ };
58
+ });
59
+ // 注册工具调用处理器
60
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
61
+ const { name, arguments: args } = request.params;
62
+ try {
63
+ switch (name) {
64
+ case "get_video_info": {
65
+ const bvidOrUrl = args?.bvid_or_url;
66
+ const preferredLang = args?.preferred_lang;
67
+ if (!bvidOrUrl) {
68
+ throw new Error("Missing required parameter: bvid_or_url");
69
+ }
70
+ const result = await getVideoInfoWithSubtitle(bvidOrUrl, preferredLang);
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text",
75
+ text: JSON.stringify(result, null, 2),
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ case "get_video_comments": {
81
+ const bvidOrUrl = args?.bvid_or_url;
82
+ const detailLevel = args?.detail_level || "brief";
83
+ if (!bvidOrUrl) {
84
+ throw new Error("Missing required parameter: bvid_or_url");
85
+ }
86
+ const result = await getVideoCommentsData(bvidOrUrl, detailLevel);
87
+ return {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: JSON.stringify(result, null, 2),
92
+ },
93
+ ],
94
+ };
95
+ }
96
+ default:
97
+ throw new Error(`Unknown tool: ${name}`);
98
+ }
99
+ }
100
+ catch (error) {
101
+ console.error(`Error executing tool ${name}:`, error);
102
+ return {
103
+ content: [
104
+ {
105
+ type: "text",
106
+ text: JSON.stringify({
107
+ error: true,
108
+ message: error instanceof Error ? error.message : "Unknown error",
109
+ }, null, 2),
110
+ },
111
+ ],
112
+ isError: true,
113
+ };
114
+ }
115
+ });
116
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@xzxzzx/bilibili-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Bilibili MCP tool for video and comment summarization",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "bilibili-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "watch": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "bilibili",
25
+ "video",
26
+ "comments",
27
+ "subtitle",
28
+ "chinese",
29
+ "b站",
30
+ "视频总结",
31
+ "评论总结"
32
+ ],
33
+ "author": "365903728-oss",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/365903728-oss/bilibili-mcp.git"
38
+ },
39
+ "homepage": "https://github.com/365903728-oss/bilibili-mcp#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/365903728-oss/bilibili-mcp/issues"
42
+ },
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
46
+ "dependencies": {
47
+ "@modelcontextprotocol/sdk": "^1.0.4"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.10.2",
51
+ "typescript": "^5.7.2"
52
+ }
53
+ }