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 +21 -0
- bilihud-0.1.0/PKG-INFO +120 -0
- bilihud-0.1.0/README.md +83 -0
- bilihud-0.1.0/pyproject.toml +73 -0
- bilihud-0.1.0/setup.cfg +4 -0
- bilihud-0.1.0/src/bilihud/__init__.py +12 -0
- bilihud-0.1.0/src/bilihud/__main__.py +4 -0
- bilihud-0.1.0/src/bilihud/danmaku_client.py +186 -0
- bilihud-0.1.0/src/bilihud/danmaku_widget.py +955 -0
- bilihud-0.1.0/src/bilihud/main.py +56 -0
- bilihud-0.1.0/src/bilihud/utils.py +69 -0
- bilihud-0.1.0/src/bilihud.egg-info/PKG-INFO +120 -0
- bilihud-0.1.0/src/bilihud.egg-info/SOURCES.txt +16 -0
- bilihud-0.1.0/src/bilihud.egg-info/dependency_links.txt +1 -0
- bilihud-0.1.0/src/bilihud.egg-info/entry_points.txt +2 -0
- bilihud-0.1.0/src/bilihud.egg-info/requires.txt +13 -0
- bilihud-0.1.0/src/bilihud.egg-info/top_level.txt +1 -0
- bilihud-0.1.0/tests/__init__.py +1 -0
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
|
+

|
|
41
|
+
[](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
|
+

|
|
52
|
+
|
|
53
|
+
### 游戏穿透模式 (Pass-through)
|
|
54
|
+

|
|
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集成库
|
bilihud-0.1.0/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# B站弹幕阅读器 (bilihud)
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](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
|
+

|
|
15
|
+
|
|
16
|
+
### 游戏穿透模式 (Pass-through)
|
|
17
|
+

|
|
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
|
bilihud-0.1.0/setup.cfg
ADDED
|
@@ -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,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
|
+
|