butterbot-python 3.1.0.dev1__py3-none-any.whl

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.
Files changed (79) hide show
  1. butterbot/__init__.py +15 -0
  2. butterbot/app/__init__.py +41 -0
  3. butterbot/app/bot_app.py +374 -0
  4. butterbot/app/config.py +156 -0
  5. butterbot/app/source_manager.py +367 -0
  6. butterbot/core/__init__.py +46 -0
  7. butterbot/core/api/__init__.py +9 -0
  8. butterbot/core/api/base_api.py +32 -0
  9. butterbot/core/context/README.md +1 -0
  10. butterbot/core/context/__init__.py +9 -0
  11. butterbot/core/context/api_registry.py +109 -0
  12. butterbot/core/context/app_context.py +51 -0
  13. butterbot/core/context/config_provider.py +14 -0
  14. butterbot/core/data/__init__.py +15 -0
  15. butterbot/core/data/base_data.py +29 -0
  16. butterbot/core/data/base_model.py +197 -0
  17. butterbot/core/event/README.md +1 -0
  18. butterbot/core/event/__init__.py +10 -0
  19. butterbot/core/event/event.py +27 -0
  20. butterbot/core/event/event_bus.py +276 -0
  21. butterbot/core/event/subscriber.py +130 -0
  22. butterbot/core/exceptions.py +90 -0
  23. butterbot/core/filter/__init__.py +11 -0
  24. butterbot/core/filter/base_filter.py +74 -0
  25. butterbot/core/source/README.md +1 -0
  26. butterbot/core/source/__init__.py +9 -0
  27. butterbot/core/source/base_source.py +134 -0
  28. butterbot/core/types/__init__.py +9 -0
  29. butterbot/core/types/base_type.py +74 -0
  30. butterbot/sources/__init__.py +1 -0
  31. butterbot/sources/bilibili/README.md +10 -0
  32. butterbot/sources/bilibili/__init__.py +20 -0
  33. butterbot/sources/bilibili/api/__init__.py +5 -0
  34. butterbot/sources/bilibili/api/bili_api.py +123 -0
  35. butterbot/sources/bilibili/data/__init__.py +52 -0
  36. butterbot/sources/bilibili/data/danmaku_gift_data.py +135 -0
  37. butterbot/sources/bilibili/data/danmaku_guard_data.py +54 -0
  38. butterbot/sources/bilibili/data/danmaku_msg_data.py +71 -0
  39. butterbot/sources/bilibili/data/dto/__init__.py +59 -0
  40. butterbot/sources/bilibili/data/dto/danmaku_gift_dto.py +193 -0
  41. butterbot/sources/bilibili/data/dto/danmaku_guard_buy_dto.py +54 -0
  42. butterbot/sources/bilibili/data/dto/danmaku_msg_dto.py +123 -0
  43. butterbot/sources/bilibili/data/dto/dynamic_dto.py +276 -0
  44. butterbot/sources/bilibili/data/dto/live_room_dto.py +169 -0
  45. butterbot/sources/bilibili/data/dto/video_part_dto.py +18 -0
  46. butterbot/sources/bilibili/data/dynamic_data.py +362 -0
  47. butterbot/sources/bilibili/data/live_room_data.py +162 -0
  48. butterbot/sources/bilibili/data/video_part.py +46 -0
  49. butterbot/sources/bilibili/source/__init__.py +15 -0
  50. butterbot/sources/bilibili/source/base_polling_source.py +130 -0
  51. butterbot/sources/bilibili/source/bili_danmaku_source.py +230 -0
  52. butterbot/sources/bilibili/source/bili_dynamic_source.py +135 -0
  53. butterbot/sources/bilibili/source/bili_live_source.py +137 -0
  54. butterbot/sources/bilibili/types/__init__.py +11 -0
  55. butterbot/sources/bilibili/types/bili_type.py +32 -0
  56. butterbot/sources/napcat/README.md +10 -0
  57. butterbot/sources/napcat/__init__.py +20 -0
  58. butterbot/sources/napcat/api/__init__.py +3 -0
  59. butterbot/sources/napcat/api/napcat_api.py +316 -0
  60. butterbot/sources/napcat/data/__init__.py +179 -0
  61. butterbot/sources/napcat/data/event_data.py +441 -0
  62. butterbot/sources/napcat/data/segment_data.py +432 -0
  63. butterbot/sources/napcat/events.py +91 -0
  64. butterbot/sources/napcat/filters/__init__.py +19 -0
  65. butterbot/sources/napcat/filters/filters.py +202 -0
  66. butterbot/sources/napcat/source/__init__.py +5 -0
  67. butterbot/sources/napcat/source/napcat_source.py +58 -0
  68. butterbot/sources/napcat/types/__init__.py +5 -0
  69. butterbot/sources/napcat/types/napcat_type.py +61 -0
  70. butterbot/utils/README.md +15 -0
  71. butterbot/utils/__init__.py +11 -0
  72. butterbot/utils/data_pair.py +27 -0
  73. butterbot/utils/logging_config.py +521 -0
  74. butterbot/utils/terminal.py +308 -0
  75. butterbot/utils/websocket.py +1270 -0
  76. butterbot_python-3.1.0.dev1.dist-info/METADATA +769 -0
  77. butterbot_python-3.1.0.dev1.dist-info/RECORD +79 -0
  78. butterbot_python-3.1.0.dev1.dist-info/WHEEL +4 -0
  79. butterbot_python-3.1.0.dev1.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,362 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from butterbot.core.data import BaseDataMixin
5
+
6
+ from .dto import (
7
+ ArticleDto,
8
+ AuthorDto,
9
+ DynamicDTO,
10
+ LiveRcmdDto,
11
+ MusicDto,
12
+ StatDto,
13
+ VideoDto,
14
+ )
15
+
16
+
17
+ def get_max_id(date: dict) -> int | None:
18
+ """获取最新动态的索引ID
19
+ Args:
20
+ date (dict): 原始动态数据
21
+ Returns:
22
+ max_id (int): 索引id,如果数据不存在则返回None
23
+
24
+ """
25
+ # 数据校验:检查 items 键是否存在
26
+ if not date or "items" not in date:
27
+ return None
28
+
29
+ list_data = date["items"]
30
+
31
+ # 数据校验:检查 items 是否为空
32
+ if not list_data:
33
+ return None
34
+
35
+ max_timestamp: int = 0
36
+ max_id: int | None = None
37
+
38
+ for index, item in enumerate(list_data):
39
+ # 数据校验:检查 item 是否为字典
40
+ if not isinstance(item, dict):
41
+ continue
42
+
43
+ # 数据校验:检查嵌套的 key 是否存在
44
+ modules = item.get("modules")
45
+ if not modules or not isinstance(modules, dict):
46
+ continue
47
+
48
+ module_author = modules.get("module_author")
49
+ if not module_author or not isinstance(module_author, dict):
50
+ continue
51
+
52
+ pub_ts = module_author.get("pub_ts")
53
+ if pub_ts is None:
54
+ continue
55
+
56
+ try:
57
+ timestamp = int(pub_ts)
58
+ if timestamp > max_timestamp:
59
+ max_timestamp = timestamp
60
+ max_id = index
61
+ except (ValueError, TypeError):
62
+ # 如果转换失败,跳过该项
63
+ continue
64
+
65
+ return max_id
66
+
67
+
68
+ @dataclass(frozen=True)
69
+ class AuthorData(BaseDataMixin):
70
+ """
71
+ 作者信息数据
72
+ """
73
+
74
+ uid: int # UP主UID
75
+ name: str # UP主昵称
76
+ face: str # UP主头像URL
77
+
78
+ @classmethod
79
+ def from_dto(cls, author: AuthorDto) -> "AuthorData":
80
+ """从AuthorDto构造AuthorData实例"""
81
+ return cls(
82
+ uid=author.uid,
83
+ name=author.name,
84
+ face=author.face,
85
+ )
86
+
87
+ @property
88
+ def jump_url(self) -> str:
89
+ """作者空间跳转链接"""
90
+ return "https://space.bilibili.com/%s" % self.uid
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class StatData(BaseDataMixin):
95
+ """
96
+ 动态统计信息数据
97
+ """
98
+
99
+ comment_count: int # 评论数
100
+ like_count: int # 点赞数
101
+ forward_count: int # 转发数
102
+
103
+ @classmethod
104
+ def from_dto(cls, stat: StatDto) -> "StatData":
105
+ """从StatDto构造StatData实例"""
106
+ return cls(
107
+ comment_count=stat.comment_count,
108
+ like_count=stat.like_count,
109
+ forward_count=stat.forward_count,
110
+ )
111
+
112
+
113
+ @dataclass(frozen=True)
114
+ class VideoData(BaseDataMixin):
115
+ """
116
+ 视频信息数据
117
+ """
118
+
119
+ av_id: str # 视频AV号
120
+ bv_id: str # 视频BV号
121
+ title: str # 视频标题
122
+ cover: str # 视频封面
123
+ desc: str # 视频简介
124
+ duration_text: str # 视频时长
125
+ dynamic_text: str # 动态文本
126
+ play_count: str # 播放数
127
+ danmaku_count: str # 弹幕数
128
+
129
+ @classmethod
130
+ def from_dto(cls, video: VideoDto) -> "VideoData":
131
+ """从VideoDto构造VideoData实例"""
132
+ return cls(
133
+ av_id=video.av_id,
134
+ bv_id=video.bv_id,
135
+ title=video.title,
136
+ cover=video.cover,
137
+ desc=video.desc,
138
+ duration_text=video.duration_text,
139
+ dynamic_text=video.dynamic_text,
140
+ play_count=video.play_count,
141
+ danmaku_count=video.danmaku_count,
142
+ )
143
+
144
+ @property
145
+ def jump_url(self) -> str:
146
+ """视频跳转链接"""
147
+ return "https://www.bilibili.com/video/%s/" % self.bv_id
148
+
149
+
150
+ @dataclass(frozen=True)
151
+ class MusicData(BaseDataMixin):
152
+ """
153
+ 音乐信息数据
154
+ """
155
+
156
+ music_id: str # 音乐ID
157
+ title: str # 音乐标题
158
+ cover: str # 音乐封面
159
+ label: str # 音乐标签(作者/歌手)
160
+ dynamic_text: str # 动态文本
161
+
162
+ @classmethod
163
+ def from_dto(cls, music: MusicDto) -> "MusicData":
164
+ """从MusicDto构造MusicData实例"""
165
+ return cls(
166
+ music_id=music.music_id,
167
+ title=music.title,
168
+ cover=music.cover,
169
+ label=music.label,
170
+ dynamic_text=music.dynamic_text,
171
+ )
172
+
173
+ @property
174
+ def jump_url(self) -> str:
175
+ """音乐跳转链接"""
176
+ return "https://www.bilibili.com/audio/au%s" % self.music_id
177
+
178
+
179
+ @dataclass(frozen=True)
180
+ class ArticleData(BaseDataMixin):
181
+ """
182
+ 专栏信息数据
183
+ """
184
+
185
+ title: str # 专栏标题
186
+ summary: str # 专栏摘要
187
+ has_more: bool # 是否有更多内容
188
+ article_id: int # 专栏ID
189
+
190
+ @classmethod
191
+ def from_dto(cls, article: ArticleDto) -> "ArticleData":
192
+ """从ArticleDto构造ArticleData实例"""
193
+ return cls(
194
+ title=article.title,
195
+ summary=article.summary,
196
+ has_more=article.has_more,
197
+ article_id=article.id,
198
+ )
199
+
200
+ @property
201
+ def jump_url(self) -> str:
202
+ """专栏跳转链接"""
203
+ return "https://www.bilibili.com/opus/%s" % self.article_id
204
+
205
+
206
+ @dataclass(frozen=True)
207
+ class LiveRcmdData(BaseDataMixin):
208
+ """
209
+ 直播推荐信息数据
210
+ """
211
+
212
+ room_id: int # 直播间ID
213
+ live_status: int # 直播状态 1:直播中
214
+ title: str # 直播间标题
215
+ cover: str # 直播间封面
216
+ online: int # 在线人数
217
+ area_id: int # 直播分区ID
218
+ area_name: str # 直播分区
219
+ parent_area_id: int # 直播父分区ID
220
+ parent_area_name: str # 直播父分区
221
+ live_start_time: int # 开播时间戳
222
+ watched_num: int | None # 观看人数
223
+ switch: bool | None # 观看榜开关
224
+ text_small: str | None # 小文本
225
+ text_large: str | None # 大文本
226
+
227
+ @classmethod
228
+ def from_dto(cls, live_rcmd: LiveRcmdDto) -> "LiveRcmdData":
229
+ """从LiveRcmdDto构造LiveRcmdData实例"""
230
+ return cls(
231
+ room_id=live_rcmd.room_id,
232
+ live_status=live_rcmd.live_status,
233
+ title=live_rcmd.title,
234
+ cover=live_rcmd.cover,
235
+ online=live_rcmd.online,
236
+ area_id=live_rcmd.area_id,
237
+ area_name=live_rcmd.area_name,
238
+ parent_area_id=live_rcmd.parent_area_id,
239
+ parent_area_name=live_rcmd.parent_area_name,
240
+ live_start_time=live_rcmd.live_start_time,
241
+ watched_num=live_rcmd.watched_num,
242
+ switch=live_rcmd.switch,
243
+ text_small=live_rcmd.text_small,
244
+ text_large=live_rcmd.text_large,
245
+ )
246
+
247
+ @property
248
+ def jump_url(self) -> str:
249
+ """直播间跳转链接"""
250
+ return "https://live.bilibili.com/%s" % self.room_id
251
+
252
+
253
+ @dataclass(frozen=True)
254
+ class DynamicData(BaseDataMixin):
255
+ """
256
+ 动态数据
257
+ """
258
+
259
+ dynamic_id: str # 动态ID
260
+ dynamic_type: str # 动态类型
261
+ visible: bool # 动态显示状态(false时被折叠)
262
+ pub_time: str # 发布时间
263
+ pub_ts: int # 发布时间戳
264
+ author: AuthorData # 作者信息
265
+ stat: StatData | None # 统计信息
266
+ tag: str | None # 标签(如置顶)
267
+ text: str | None # 文字内容
268
+ pics_url: list[str] | None # 图片列表
269
+ video: VideoData | None # 视频信息
270
+ music: MusicData | None # 音乐信息
271
+ article: ArticleData | None # 专栏信息
272
+ live_rcmd: LiveRcmdData | None # 直播推荐信息
273
+ forward_orig: Optional["DynamicData"] # 转发的原动态
274
+
275
+ @classmethod
276
+ def from_dto(cls, dto: DynamicDTO) -> "DynamicData":
277
+ """从DTO对象构造DynamicData实例
278
+
279
+ Args:
280
+ dto: DynamicDTO对象
281
+
282
+ Returns:
283
+ DynamicData实例
284
+ """
285
+ # 构造作者数据
286
+ author_data = AuthorData.from_dto(dto.author)
287
+
288
+ # 构造统计数据
289
+ stat_data = StatData.from_dto(dto.stat) if dto.stat else None
290
+
291
+ # 构造视频数据
292
+ video_data = VideoData.from_dto(dto.video) if dto.video else None
293
+
294
+ # 构造音乐数据
295
+ music_data = MusicData.from_dto(dto.music) if dto.music else None
296
+
297
+ # 构造专栏数据
298
+ article_data = ArticleData.from_dto(dto.article) if dto.article else None
299
+
300
+ # 构造直播推荐数据
301
+ live_rcmd_data = LiveRcmdData.from_dto(dto.live_rcmd) if dto.live_rcmd else None
302
+
303
+ # 递归构造转发动态数据
304
+ forward_orig_data = cls.from_dto(dto.forward_orig) if dto.forward_orig else None
305
+
306
+ return cls(
307
+ dynamic_id=dto.dynamic_id,
308
+ dynamic_type=dto.dynamic_type,
309
+ visible=dto.visible,
310
+ pub_time=dto.pub_time,
311
+ pub_ts=dto.pub_ts,
312
+ author=author_data,
313
+ stat=stat_data,
314
+ tag=dto.tag,
315
+ text=dto.text,
316
+ pics_url=list(dto.pics_url) if dto.pics_url else None,
317
+ video=video_data,
318
+ music=music_data,
319
+ article=article_data,
320
+ live_rcmd=live_rcmd_data,
321
+ forward_orig=forward_orig_data,
322
+ )
323
+
324
+ @property
325
+ def jump_url(self) -> str:
326
+ """动态跳转链接"""
327
+ return "https://t.bilibili.com/%s" % self.dynamic_id
328
+
329
+ @property
330
+ def is_live(self) -> bool:
331
+ """判断动态是否为直播推荐类型"""
332
+ return self.dynamic_type == "DYNAMIC_TYPE_LIVE_RCMD"
333
+
334
+ @property
335
+ def is_video(self) -> bool:
336
+ """判断动态是否为视频类型"""
337
+ return self.dynamic_type == "DYNAMIC_TYPE_AV"
338
+
339
+ @property
340
+ def is_music(self) -> bool:
341
+ """判断动态是否为音乐类型"""
342
+ return self.dynamic_type == "DYNAMIC_TYPE_MUSIC"
343
+
344
+ @property
345
+ def is_article(self) -> bool:
346
+ """判断动态是否为专栏类型"""
347
+ return self.dynamic_type == "DYNAMIC_TYPE_ARTICLE"
348
+
349
+ @property
350
+ def is_word(self) -> bool:
351
+ """判断动态是否为纯文字类型"""
352
+ return self.dynamic_type == "DYNAMIC_TYPE_WORD"
353
+
354
+ @property
355
+ def is_draw(self) -> bool:
356
+ """判断动态是否为图文类型"""
357
+ return self.dynamic_type == "DYNAMIC_TYPE_DRAW"
358
+
359
+ @property
360
+ def is_forward(self) -> bool:
361
+ """判断动态是否为转发类型"""
362
+ return self.dynamic_type == "DYNAMIC_TYPE_FORWARD"
@@ -0,0 +1,162 @@
1
+ from dataclasses import dataclass
2
+
3
+ from butterbot.core.data import BaseDataMixin
4
+
5
+ from .dto import (
6
+ AnchorInfoDto,
7
+ LiveRoomDTO,
8
+ NoticeBoardDto,
9
+ RoomInfoDto,
10
+ WatchedShowDto,
11
+ )
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class RoomInfoData(BaseDataMixin):
16
+ """直播间信息数据"""
17
+
18
+ uid: int # 用户uid
19
+ room_id: int # 房间号
20
+ title: str # 直播间标题
21
+ cover_url: str # 直播间封面url
22
+ background_url: str # 直播间背景图url
23
+ description: str # 主播简介
24
+ tags: list[str] # 直播间标签列表
25
+ live_status: int # 直播状态 0:未开播 1:直播中 2:轮播中
26
+ live_start_time: int # 直播开始时间戳
27
+ parent_area_name: str # 直播间父分区
28
+ parent_area_id: int # 直播间父分区ID
29
+ area_name: str # 直播间子分区
30
+ area_id: int # 直播间子分区ID
31
+ keyframe_url: str # 直播间关键帧url
32
+ online: int # 直播间当前在线人数
33
+
34
+ @classmethod
35
+ def from_dto(cls, room_info: RoomInfoDto) -> "RoomInfoData":
36
+ """从RoomInfoDto构造RoomInfoData实例"""
37
+ return cls(
38
+ uid=room_info.uid,
39
+ room_id=room_info.room_id,
40
+ title=room_info.title,
41
+ cover_url=room_info.cover_url,
42
+ background_url=room_info.background_url,
43
+ description=room_info.description,
44
+ tags=list(room_info.tags),
45
+ live_status=room_info.live_status,
46
+ live_start_time=room_info.live_start_time,
47
+ parent_area_name=room_info.parent_area_name,
48
+ parent_area_id=room_info.parent_area_id,
49
+ area_name=room_info.area_name,
50
+ area_id=room_info.area_id,
51
+ keyframe_url=room_info.keyframe_url,
52
+ online=room_info.online,
53
+ )
54
+
55
+ @property
56
+ def jump_url(self) -> str:
57
+ """直播间跳转链接"""
58
+ return "https://live.bilibili.com/%s" % self.room_id
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class AnchorInfoData(BaseDataMixin):
63
+ """主播信息数据"""
64
+
65
+ name: str # 主播昵称
66
+ face_url: str # 主播头像url
67
+ gender: str # 主播性别
68
+ official_info: str # 主播官方信息(认证信息)
69
+ fanclub_name: str # 粉丝牌名称
70
+ fanclub_num: int # 粉丝团人数
71
+ live_level: int # 主播等级
72
+ live_score: int # 直播分数
73
+ live_upgrade_score: int # 升级所需分数
74
+
75
+ @classmethod
76
+ def from_dto(cls, anchor_info: AnchorInfoDto) -> "AnchorInfoData":
77
+ """从AnchorInfoDto构造AnchorInfoData实例"""
78
+ return cls(
79
+ name=anchor_info.name,
80
+ face_url=anchor_info.face_url,
81
+ gender=anchor_info.gender,
82
+ official_info=anchor_info.official_info,
83
+ fanclub_name=anchor_info.fanclub_name,
84
+ fanclub_num=anchor_info.fanclub_num,
85
+ live_level=anchor_info.live_level,
86
+ live_score=anchor_info.live_score,
87
+ live_upgrade_score=anchor_info.live_upgrade_score,
88
+ )
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class WatchedShowData(BaseDataMixin):
93
+ """观看榜信息数据"""
94
+
95
+ switch: bool # 观看榜开关
96
+ num: int # 观看人数/人气值
97
+ text_small: str # 小文本
98
+ text_large: str # 大文本
99
+
100
+ @classmethod
101
+ def from_dto(cls, watched_show: WatchedShowDto) -> "WatchedShowData":
102
+ """从WatchedShowDto构造WatchedShowData实例"""
103
+ return cls(
104
+ switch=watched_show.switch,
105
+ num=watched_show.num,
106
+ text_small=watched_show.text_small,
107
+ text_large=watched_show.text_large,
108
+ )
109
+
110
+
111
+ @dataclass(frozen=True)
112
+ class NoticeBoardData(BaseDataMixin):
113
+ """公告栏信息数据"""
114
+
115
+ content: str # 公告内容
116
+ ctime: str # 公告发布时间
117
+
118
+ @classmethod
119
+ def from_dto(cls, notice_board: NoticeBoardDto) -> "NoticeBoardData":
120
+ """从NoticeBoardDto构造NoticeBoardData实例"""
121
+ return cls(content=notice_board.content, ctime=notice_board.ctime)
122
+
123
+
124
+ @dataclass(frozen=True)
125
+ class LiveRoomData(BaseDataMixin):
126
+ """直播间数据"""
127
+
128
+ room_info: RoomInfoData # 直播间信息
129
+ anchor_info: AnchorInfoData # 主播信息
130
+ watched_show: WatchedShowData # 观看榜信息
131
+ notice_board: NoticeBoardData | None # 公告栏信息
132
+
133
+ @classmethod
134
+ def from_dto(cls, dto: LiveRoomDTO) -> "LiveRoomData":
135
+ """从DTO对象构造LiveRoomData实例
136
+
137
+ Args:
138
+ dto: LiveRoomDTO对象
139
+
140
+ Returns:
141
+ LiveRoomData实例
142
+ """
143
+ # 构造直播间信息数据
144
+ room_info_data = RoomInfoData.from_dto(dto.room_info)
145
+
146
+ # 构造主播信息数据
147
+ anchor_info_data = AnchorInfoData.from_dto(dto.anchor_info)
148
+
149
+ # 构造观看榜信息数据
150
+ watched_show_data = WatchedShowData.from_dto(dto.watched_show)
151
+
152
+ # 构造公告栏信息数据
153
+ notice_board_data = (
154
+ NoticeBoardData.from_dto(dto.notice_board) if dto.notice_board else None
155
+ )
156
+
157
+ return cls(
158
+ room_info=room_info_data,
159
+ anchor_info=anchor_info_data,
160
+ watched_show=watched_show_data,
161
+ notice_board=notice_board_data,
162
+ )
@@ -0,0 +1,46 @@
1
+ from dataclasses import dataclass
2
+
3
+ from butterbot.core.data import BaseDataMixin
4
+
5
+ from .dto import (
6
+ VideoPartDto,
7
+ )
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class VideoPartData(BaseDataMixin):
12
+ """
13
+ 视频字幕数据
14
+ """
15
+
16
+ start_timestamp: int # 开始时间戳
17
+ end_timestamp: int # 结束时间戳
18
+ content: str # 字幕内容
19
+
20
+ @classmethod
21
+ def from_dto_list(cls, dto_list: list[VideoPartDto]) -> list["VideoPartData"]:
22
+ """从DTO对象列表构造VideoPartData实例列表
23
+
24
+ Args:
25
+ dto_list: VideoPartDto对象列表
26
+
27
+ Returns:
28
+ VideoPartData实例列表
29
+ """
30
+ return [cls.from_dto(dto) for dto in dto_list]
31
+
32
+ @classmethod
33
+ def from_dto(cls, dto: VideoPartDto) -> "VideoPartData":
34
+ """从DTO对象构造VideoPartData实例
35
+
36
+ Args:
37
+ dto: VideoPartDto对象
38
+
39
+ Returns:
40
+ VideoPartData实例
41
+ """
42
+ return cls(
43
+ start_timestamp=dto.start_timestamp,
44
+ end_timestamp=dto.end_timestamp,
45
+ content=dto.content,
46
+ )
@@ -0,0 +1,15 @@
1
+ from .bili_danmaku_source import (
2
+ BiliDanmakuSource,
3
+ )
4
+ from .bili_dynamic_source import (
5
+ BiliDynamicSource,
6
+ )
7
+ from .bili_live_source import (
8
+ BiliLiveSource,
9
+ )
10
+
11
+ __all__ = [
12
+ "BiliDanmakuSource",
13
+ "BiliDynamicSource",
14
+ "BiliLiveSource",
15
+ ]