bilihud 0.1.0__tar.gz

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.
bilihud-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025, Locez
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.
bilihud-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: bilihud
3
+ Version: 0.1.0
4
+ Summary: B站弹幕阅读器 - 一个可以在游戏全屏时显示弹幕的Qt应用程序
5
+ Author-email: Locez <locez@locez.com>
6
+ Maintainer-email: Locez <locez@locez.com>
7
+ License: MIT
8
+ Project-URL: bugs, https://github.com/locez/bilihud/issues
9
+ Project-URL: changelog, https://github.com/locez/bilihud/blob/master/changelog.md
10
+ Project-URL: homepage, https://github.com/locez/bilihud
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Natural Language :: Chinese (Simplified)
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Multimedia :: Video
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: PyQt6
25
+ Requires-Dist: aiohttp
26
+ Requires-Dist: qasync
27
+ Requires-Dist: browser-cookie3
28
+ Requires-Dist: brotli
29
+ Requires-Dist: pure_protobuf
30
+ Provides-Extra: test
31
+ Requires-Dist: coverage; extra == "test"
32
+ Requires-Dist: pytest; extra == "test"
33
+ Requires-Dist: ruff; extra == "test"
34
+ Requires-Dist: ty; extra == "test"
35
+ Requires-Dist: ipdb; extra == "test"
36
+ Dynamic: license-file
37
+
38
+ # B站弹幕阅读器 (bilihud)
39
+
40
+ ![PyPI version](https://img.shields.io/pypi/v/bilihud.svg)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+
43
+ 一个基于PyQt6和blivedm的B站弹幕阅读器,可以在Linux KDE环境下全屏游戏时显示弹幕。
44
+
45
+ > [!NOTE]
46
+ > 本项目基于 **Vibe Coding**(氛围驱动编码)模式开发,旨在快速实现创意与功能。目前仅在有限环境下进行过测试,未做大量的严谨验证。如有 Bug,欢迎反馈!
47
+
48
+ ## 效果预览
49
+
50
+ ### 一般模式 (Normal)
51
+ ![Normal Mode](screenshots/normal.png)
52
+
53
+ ### 游戏穿透模式 (Pass-through)
54
+ ![Pass-through Mode](screenshots/passthrough.png)
55
+
56
+ ## 功能特点
57
+
58
+ * 实时显示B站直播间弹幕
59
+ * 半透明overlay窗口,可在游戏全屏时显示
60
+ * 美观的UI界面,支持不同用户等级的颜色标识
61
+ * 支持连接/断开直播间
62
+ * 显示用户名、舰长/VIP标识
63
+ * **注意:** 仅支持 **X11** 环境(推荐 KDE X11)。由于 Wayland 的安全机制,无法实现完美的鼠标穿透(Pass-through)模式,因此暂不支持纯 Wayland 环境。
64
+
65
+ ## 极速上手
66
+
67
+ ### 1. 安装
68
+
69
+ ```bash
70
+ # 1. 克隆仓库
71
+ git clone https://github.com/locez/bilihud.git
72
+ cd bilihud
73
+
74
+ # 2. 初始化子模块 (blivedm)
75
+ git submodule update --init --recursive
76
+
77
+ # 3. 环境配置与安装 (推荐使用 uv)
78
+ # 安装 uv
79
+ pip install uv
80
+
81
+ # 创建虚拟环境并同步依赖
82
+ uv sync
83
+
84
+ # 激活环境
85
+ source .venv/bin/activate
86
+ ```
87
+
88
+ ### 2. 启动
89
+
90
+ ```bash
91
+ python -m src.bilihud.main
92
+ ```
93
+
94
+ ## 隐私说明 & 配置
95
+
96
+ ### 自动登录 (Cookies)
97
+
98
+ 为了提供完整的体验(如显示完整用户名、发送弹幕、显示舰长标识),**BiliHUD** 会尝试自动读取本地浏览器的 Bilibili 登录状态。
99
+
100
+ * **读取范围**: 程序仅读取 `.bilibili.com` 域下的 Cookies。
101
+ * **读取目的**: 获取 `SESSDATA` 和 `bili_jct` (CSRF Token) 仅用于与 Bilibili API 进行必要的身份验证。
102
+ * **支持浏览器**: Chrome, Edge, Firefox。
103
+ * **数据安全**: 您的 Cookies 仅在本地内存中使用,**绝不会**被发送到任何第三方服务器。
104
+
105
+ ### 手动配置 (可选)
106
+
107
+ 如果自动读取失败,或者您不希望程序读取浏览器 Cookies,可以在代码中手动传入 `SESSDATA`:
108
+
109
+ ```python
110
+ from bilihud.danmaku_widget import DanmakuWidget
111
+
112
+ # 在创建窗口时手动传入 SESSDATA
113
+ danmaku_widget = DanmakuWidget(room_id=123456, sessdata="你的SESSDATA")
114
+ ```
115
+
116
+ ## 鸣谢
117
+
118
+ * [blivedm](https://github.com/xfgryujk/blivedm) - B站直播弹幕协议库
119
+ * [PyQt6](https://pypi.org/project/PyQt6/) - Python GUI框架
120
+ * [qasync](https://github.com/CabbageDevelopment/qasync) - PyQt6与asyncio集成库
@@ -0,0 +1,83 @@
1
+ # B站弹幕阅读器 (bilihud)
2
+
3
+ ![PyPI version](https://img.shields.io/pypi/v/bilihud.svg)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ 一个基于PyQt6和blivedm的B站弹幕阅读器,可以在Linux KDE环境下全屏游戏时显示弹幕。
7
+
8
+ > [!NOTE]
9
+ > 本项目基于 **Vibe Coding**(氛围驱动编码)模式开发,旨在快速实现创意与功能。目前仅在有限环境下进行过测试,未做大量的严谨验证。如有 Bug,欢迎反馈!
10
+
11
+ ## 效果预览
12
+
13
+ ### 一般模式 (Normal)
14
+ ![Normal Mode](screenshots/normal.png)
15
+
16
+ ### 游戏穿透模式 (Pass-through)
17
+ ![Pass-through Mode](screenshots/passthrough.png)
18
+
19
+ ## 功能特点
20
+
21
+ * 实时显示B站直播间弹幕
22
+ * 半透明overlay窗口,可在游戏全屏时显示
23
+ * 美观的UI界面,支持不同用户等级的颜色标识
24
+ * 支持连接/断开直播间
25
+ * 显示用户名、舰长/VIP标识
26
+ * **注意:** 仅支持 **X11** 环境(推荐 KDE X11)。由于 Wayland 的安全机制,无法实现完美的鼠标穿透(Pass-through)模式,因此暂不支持纯 Wayland 环境。
27
+
28
+ ## 极速上手
29
+
30
+ ### 1. 安装
31
+
32
+ ```bash
33
+ # 1. 克隆仓库
34
+ git clone https://github.com/locez/bilihud.git
35
+ cd bilihud
36
+
37
+ # 2. 初始化子模块 (blivedm)
38
+ git submodule update --init --recursive
39
+
40
+ # 3. 环境配置与安装 (推荐使用 uv)
41
+ # 安装 uv
42
+ pip install uv
43
+
44
+ # 创建虚拟环境并同步依赖
45
+ uv sync
46
+
47
+ # 激活环境
48
+ source .venv/bin/activate
49
+ ```
50
+
51
+ ### 2. 启动
52
+
53
+ ```bash
54
+ python -m src.bilihud.main
55
+ ```
56
+
57
+ ## 隐私说明 & 配置
58
+
59
+ ### 自动登录 (Cookies)
60
+
61
+ 为了提供完整的体验(如显示完整用户名、发送弹幕、显示舰长标识),**BiliHUD** 会尝试自动读取本地浏览器的 Bilibili 登录状态。
62
+
63
+ * **读取范围**: 程序仅读取 `.bilibili.com` 域下的 Cookies。
64
+ * **读取目的**: 获取 `SESSDATA` 和 `bili_jct` (CSRF Token) 仅用于与 Bilibili API 进行必要的身份验证。
65
+ * **支持浏览器**: Chrome, Edge, Firefox。
66
+ * **数据安全**: 您的 Cookies 仅在本地内存中使用,**绝不会**被发送到任何第三方服务器。
67
+
68
+ ### 手动配置 (可选)
69
+
70
+ 如果自动读取失败,或者您不希望程序读取浏览器 Cookies,可以在代码中手动传入 `SESSDATA`:
71
+
72
+ ```python
73
+ from bilihud.danmaku_widget import DanmakuWidget
74
+
75
+ # 在创建窗口时手动传入 SESSDATA
76
+ danmaku_widget = DanmakuWidget(room_id=123456, sessdata="你的SESSDATA")
77
+ ```
78
+
79
+ ## 鸣谢
80
+
81
+ * [blivedm](https://github.com/xfgryujk/blivedm) - B站直播弹幕协议库
82
+ * [PyQt6](https://pypi.org/project/PyQt6/) - Python GUI框架
83
+ * [qasync](https://github.com/CabbageDevelopment/qasync) - PyQt6与asyncio集成库
@@ -0,0 +1,73 @@
1
+ [project]
2
+ name = "bilihud"
3
+ version = "0.1.0"
4
+ description = "B站弹幕阅读器 - 一个可以在游戏全屏时显示弹幕的Qt应用程序"
5
+ readme = "README.md"
6
+ authors = [
7
+ {name = "Locez", email = "locez@locez.com"}
8
+ ]
9
+ maintainers = [
10
+ {name = "Locez", email = "locez@locez.com"}
11
+ ]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: End Users/Desktop",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Natural Language :: Chinese (Simplified)",
17
+ "Operating System :: POSIX :: Linux",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Multimedia :: Video"
23
+ ]
24
+ license = {text = "MIT"}
25
+ dependencies = [
26
+ "PyQt6",
27
+ "aiohttp",
28
+ "qasync",
29
+ "browser-cookie3",
30
+
31
+ # for blivedm
32
+ "brotli",
33
+ "pure_protobuf"
34
+ ]
35
+ requires-python = ">= 3.10"
36
+
37
+ [project.optional-dependencies]
38
+ test = [
39
+ "coverage", # testing
40
+ "pytest", # testing
41
+ "ruff", # linting
42
+ "ty", # checking types
43
+ "ipdb", # debugging
44
+ ]
45
+
46
+ [project.urls]
47
+ bugs = "https://github.com/locez/bilihud/issues"
48
+ changelog = "https://github.com/locez/bilihud/blob/master/changelog.md"
49
+ homepage = "https://github.com/locez/bilihud"
50
+
51
+ [project.scripts]
52
+ bilihud = "bilihud.main:entry_point"
53
+
54
+ [tool.ty]
55
+ # All rules are enabled as "error" by default; no need to specify unless overriding.
56
+ # Example override: relax a rule for the entire project (uncomment if needed).
57
+ # rules.TY015 = "warn" # For invalid-argument-type, warn instead of error.
58
+
59
+ [tool.ruff]
60
+ line-length = 120
61
+
62
+ [tool.ruff.lint]
63
+ select = [
64
+ "E", # pycodestyle errors
65
+ "W", # pycodestyle warnings
66
+ "F", # Pyflakes
67
+ "I", # isort
68
+ "B", # flake8-bugbear
69
+ "UP", # pyupgrade
70
+ ]
71
+
72
+ [tool.uv]
73
+ package = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """Top-level package for bilihud."""
2
+
3
+ __author__ = """Locez"""
4
+ __email__ = 'locez@locez.com'
5
+ __version__ = '0.1.0'
6
+
7
+ # 为了确保能够找到vendor中的blivedm库,添加到sys.path
8
+ import sys
9
+ import os
10
+ _vendor_path = os.path.join(os.path.dirname(__file__), '..', '..', 'vendor', 'blivedm')
11
+ if _vendor_path not in sys.path:
12
+ sys.path.insert(0, _vendor_path)
@@ -0,0 +1,4 @@
1
+ from .main import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,186 @@
1
+ # -*- coding: utf-8 -*-
2
+ import asyncio
3
+ import http.cookies
4
+ from typing import Optional, Callable
5
+ import aiohttp
6
+ import blivedm
7
+ import blivedm.models.web as web_models
8
+
9
+
10
+ class DanmakuClient:
11
+ """
12
+ 弹幕客户端,用于获取B站直播弹幕
13
+ """
14
+
15
+ def __init__(self, room_id: int, sessdata: str = ''):
16
+ self.room_id = room_id
17
+ self.sessdata = sessdata
18
+ self.session: Optional[aiohttp.ClientSession] = None
19
+ self.client: Optional[blivedm.BLiveClient] = None
20
+ self.handler: Optional[DanmakuHandler] = None
21
+ self.on_danmaku_received: Optional[Callable[[web_models.DanmakuMessage], None]] = None
22
+ self.on_gift_received: Optional[Callable[[web_models.GiftMessage], None]] = None
23
+ self.on_interact_received: Optional[Callable[[web_models.InteractWordV2Message], None]] = None
24
+
25
+ def set_danmaku_callback(self, callback: Callable[[web_models.DanmakuMessage], None]):
26
+ """设置弹幕接收回调函数"""
27
+ self.on_danmaku_received = callback
28
+
29
+ def set_gift_callback(self, callback: Callable[[web_models.GiftMessage], None]):
30
+ """设置礼物接收回调函数"""
31
+ self.on_gift_received = callback
32
+
33
+ def set_interact_callback(self, callback: Callable[[web_models.InteractWordV2Message], None]):
34
+ """设置互动接收回调函数 (进房/关注)"""
35
+ self.on_interact_received = callback
36
+
37
+ async def start(self):
38
+ """在事件循环中启动弹幕客户端"""
39
+ # 初始化session
40
+ cookies = http.cookies.SimpleCookie()
41
+
42
+ # 尝试从浏览器加载Cookies
43
+ browser_cookies = load_bilibili_cookies()
44
+ if browser_cookies:
45
+ # print("Successfully loaded cookies from browser")
46
+ # 将http.cookiejar.CookieJar转换为SimpleCookie或简单dict
47
+ for cookie in browser_cookies:
48
+ cookies[cookie.name] = cookie.value
49
+
50
+ if self.sessdata:
51
+ cookies['SESSDATA'] = self.sessdata
52
+
53
+ # 确保domain设置正确
54
+ if 'SESSDATA' in cookies:
55
+ cookies['SESSDATA']['domain'] = 'bilibili.com'
56
+
57
+ headers = {
58
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
59
+ }
60
+ self.session = aiohttp.ClientSession(headers=headers)
61
+ self.session.cookie_jar.update_cookies(cookies)
62
+
63
+ # 创建客户端和处理器
64
+ self.client = blivedm.BLiveClient(self.room_id, session=self.session)
65
+ self.handler = DanmakuHandler()
66
+ self.handler.set_danmaku_client(self)
67
+ self.client.set_handler(self.handler)
68
+
69
+ loop = asyncio.get_event_loop()
70
+ loop.call_soon(self.client.start)
71
+ # 启动客户端
72
+ # self.client.start()
73
+ # await asyncio.sleep(0.1) # 让出控制权以启动客户端
74
+
75
+ async def send_danmaku(self, message: str) -> tuple[bool, str]:
76
+ """发送弹幕"""
77
+ if not self.session or not message:
78
+ return False, "会话未初始化或消息为空"
79
+
80
+ url = 'https://api.live.bilibili.com/msg/send'
81
+
82
+ # 从cookie中获取csrf token (bili_jct)
83
+ csrf_token = ''
84
+ for cookie in self.session.cookie_jar:
85
+ if cookie.key == 'bili_jct':
86
+ csrf_token = cookie.value
87
+ break
88
+
89
+ if not csrf_token:
90
+ # print("Error: No csrf_token found in cookies")
91
+ return False, "未找到CSRF Token,请重新连接或检查Cookie"
92
+
93
+ data = {
94
+ 'bubble': '0',
95
+ 'msg': message,
96
+ 'color': '16777215',
97
+ 'mode': '1',
98
+ 'fontsize': '25',
99
+ 'rnd': str(int(asyncio.get_event_loop().time())),
100
+ 'roomid': self.room_id,
101
+ 'csrf': csrf_token,
102
+ 'csrf_token': csrf_token,
103
+ }
104
+
105
+ try:
106
+ async with self.session.post(url, data=data) as res:
107
+ if res.status != 200:
108
+ print(f"Send danmaku HTTP error: {res.status}")
109
+ return False, f"HTTP错误: {res.status}"
110
+ json_data = await res.json()
111
+ if json_data['code'] == 0:
112
+ return True, "发送成功"
113
+ else:
114
+ print(f"Send danmaku failed: {json_data['message']}")
115
+ return False, f"发送失败: {json_data['message']}"
116
+ except Exception as e:
117
+ print(f"Send danmaku exception: {e}")
118
+ return False, f"发送异常: {str(e)}"
119
+
120
+ async def stop(self):
121
+ """停止弹幕客户端"""
122
+ if self.client:
123
+ await self.client.stop_and_close()
124
+ if self.session:
125
+ await self.session.close()
126
+
127
+
128
+ class DanmakuHandler(blivedm.BaseHandler):
129
+ """弹幕处理器"""
130
+
131
+ def __init__(self):
132
+ super().__init__()
133
+ self.danmaku_client: Optional[DanmakuClient] = None
134
+
135
+ def set_danmaku_client(self, client: DanmakuClient):
136
+ self.danmaku_client = client
137
+
138
+ def _on_danmaku(self, client: blivedm.BLiveClient, message: web_models.DanmakuMessage):
139
+ """处理弹幕消息"""
140
+ if self.danmaku_client and self.danmaku_client.on_danmaku_received:
141
+ self.danmaku_client.on_danmaku_received(message)
142
+
143
+ def _on_gift(self, client: blivedm.BLiveClient, message: web_models.GiftMessage):
144
+ """处理礼物消息"""
145
+ if self.danmaku_client and self.danmaku_client.on_gift_received:
146
+ self.danmaku_client.on_gift_received(message)
147
+
148
+ def _on_interact_word_v2(self, client: blivedm.BLiveClient, message: web_models.InteractWordV2Message):
149
+ """处理进入房间/关注"""
150
+ if self.danmaku_client and self.danmaku_client.on_interact_received:
151
+ self.danmaku_client.on_interact_received(message)
152
+
153
+ def _on_super_chat(self, client: blivedm.BLiveClient, message: web_models.SuperChatMessage):
154
+ """处理醒目留言"""
155
+ # 可以在这里处理醒目留言
156
+ pass
157
+
158
+
159
+ def load_bilibili_cookies():
160
+ """尝试从浏览器加载B站Cookies"""
161
+ try:
162
+ import browser_cookie3
163
+ # 尝试从Chrome加载
164
+ # print("Attempting to load cookies from Chrome...")
165
+ cj = browser_cookie3.chrome(domain_name='.bilibili.com')
166
+ return cj
167
+ except Exception as e:
168
+ print(f"Chrome cookies failed: {e}")
169
+ try:
170
+ import browser_cookie3
171
+ # 尝试从Edge加载
172
+ cj = browser_cookie3.edge(domain_name='.bilibili.com')
173
+ return cj
174
+ except Exception as e:
175
+ print(f"Edge cookies failed: {e}")
176
+ try:
177
+ import browser_cookie3
178
+ # 尝试从Firefox加载
179
+ cj = browser_cookie3.firefox(domain_name='.bilibili.com')
180
+ return cj
181
+ except Exception as e:
182
+ print(f"Firefox cookies failed: {e}")
183
+ return None
184
+
185
+
186
+