nonebot-plugin-parser 2.5.0__tar.gz → 2.5.2__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.
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/PKG-INFO +3 -3
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/README.md +1 -1
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/pyproject.toml +6 -6
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/download/__init__.py +19 -3
- nonebot_plugin_parser-2.5.2/src/nonebot_plugin_parser/download/task.py +92 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/__init__.py +2 -2
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/acfun/__init__.py +5 -2
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/base.py +41 -15
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +23 -15
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/data.py +55 -91
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/__init__.py +26 -26
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/kuaishou/__init__.py +13 -13
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/nga.py +11 -19
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/tiktok.py +8 -4
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/twitter.py +29 -41
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/__init__.py +20 -18
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/__init__.py +24 -25
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/youtube/__init__.py +11 -32
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/__init__.py +6 -11
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/base.py +48 -35
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/common.py +292 -211
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/default.py +9 -12
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/htmlrender.py +9 -6
- nonebot_plugin_parser-2.5.2/src/nonebot_plugin_parser/renders/templates/card.html.jinja2 +668 -0
- nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/download/task.py +0 -19
- nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/renders/templates/card.html.jinja +0 -606
- nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/renders/weibo.py +0 -18
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/__init__.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/config.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/constants.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/exception.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/helper.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/filter.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/rule.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/__init__.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/acfun/video.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/slides.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/video.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/kuaishou/states.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/article.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/common.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/show.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/common.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/discovery.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/explore.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/youtube/meta.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW.ttf +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/__init__.py +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/avatar.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/play.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
- {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nonebot-plugin-parser
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.2
|
|
4
4
|
Summary: NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA
|
|
5
5
|
Keywords: acfun,bilibili,douyin,kuaishou,nga,nonebot,nonebot2,tiktok,twitter,video,weibo,xiaohongshu,youtube
|
|
6
6
|
Author: fllesser
|
|
@@ -35,7 +35,7 @@ Requires-Dist: nonebot-plugin-localstore>=0.7.4,<1.0.0
|
|
|
35
35
|
Requires-Dist: nonebot-plugin-uninfo>=0.10.1,<1.0.0
|
|
36
36
|
Requires-Dist: nonebot-plugin-htmlkit>=0.1.0rc4 ; extra == 'all'
|
|
37
37
|
Requires-Dist: nonebot-plugin-htmlrender>=0.6.7 ; extra == 'all'
|
|
38
|
-
Requires-Dist: yt-dlp[default]>=2026.
|
|
38
|
+
Requires-Dist: yt-dlp[default]>=2026.3.13 ; extra == 'all'
|
|
39
39
|
Requires-Dist: emosvg>=0.1.7 ; extra == 'all'
|
|
40
40
|
Requires-Dist: emosvg>=0.1.7 ; extra == 'emosvg'
|
|
41
41
|
Requires-Dist: nonebot-plugin-htmlkit>=0.1.0rc4 ; extra == 'htmlkit'
|
|
@@ -199,7 +199,7 @@ Description-Content-Type: text/markdown
|
|
|
199
199
|
|
|
200
200
|
uv add "nonebot-plugin-parser[htmlkit]"
|
|
201
201
|
|
|
202
|
-
`htmlrender`, 使用 `playwright` 渲染 `html`,
|
|
202
|
+
`htmlrender`, 使用 `playwright` 渲染 `html`, 插件自 `v2.5.0` 起已正式支持
|
|
203
203
|
|
|
204
204
|
uv add "nonebot-plugin-parser[htmlrender]"
|
|
205
205
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nonebot-plugin-parser"
|
|
3
|
-
version = "2.5.
|
|
3
|
+
version = "2.5.2"
|
|
4
4
|
description = "NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -68,7 +68,7 @@ emosvg = ["emosvg>=0.1.7"]
|
|
|
68
68
|
all = [
|
|
69
69
|
"nonebot-plugin-htmlkit>=0.1.0rc4",
|
|
70
70
|
"nonebot-plugin-htmlrender>=0.6.7",
|
|
71
|
-
"yt-dlp[default]>=2026.
|
|
71
|
+
"yt-dlp[default]>=2026.3.13",
|
|
72
72
|
"emosvg>=0.1.7",
|
|
73
73
|
]
|
|
74
74
|
|
|
@@ -76,7 +76,7 @@ all = [
|
|
|
76
76
|
dev = [
|
|
77
77
|
"nonebot2[fastapi]>=2.4.3,<3.0.0",
|
|
78
78
|
"nonebot-adapter-onebot>=2.4.6",
|
|
79
|
-
"ruff>=0.15.
|
|
79
|
+
"ruff>=0.15.6,<1.0.0",
|
|
80
80
|
{ include-group = "extras" },
|
|
81
81
|
{ include-group = "test" },
|
|
82
82
|
]
|
|
@@ -84,8 +84,8 @@ extras = [
|
|
|
84
84
|
"emosvg>=0.1.7",
|
|
85
85
|
"nonebot-plugin-htmlkit>=0.1.0rc4",
|
|
86
86
|
"nonebot-plugin-htmlrender>=0.6.7",
|
|
87
|
-
"yt-dlp[default]>=2026.
|
|
88
|
-
"types-yt-dlp>=2026.
|
|
87
|
+
"yt-dlp[default]>=2026.3.13",
|
|
88
|
+
"types-yt-dlp>=2026.3.13.20260314",
|
|
89
89
|
]
|
|
90
90
|
test = [
|
|
91
91
|
"nonebug>=0.4.3,<1.0.0",
|
|
@@ -118,7 +118,7 @@ nonebug = { git = "https://github.com/nonebot/nonebug", rev = "master" }
|
|
|
118
118
|
[tool.bumpversion]
|
|
119
119
|
tag = true
|
|
120
120
|
commit = true
|
|
121
|
-
current_version = "2.5.
|
|
121
|
+
current_version = "2.5.2"
|
|
122
122
|
message = "release: bump vesion from {current_version} to {new_version}"
|
|
123
123
|
|
|
124
124
|
[[tool.bumpversion.files]]
|
|
@@ -98,7 +98,13 @@ class StreamDownloader:
|
|
|
98
98
|
"""download video file by url with stream"""
|
|
99
99
|
if video_name is None:
|
|
100
100
|
video_name = generate_file_name(url, ".mp4")
|
|
101
|
-
|
|
101
|
+
|
|
102
|
+
return await self.download_file(
|
|
103
|
+
url,
|
|
104
|
+
file_name=video_name,
|
|
105
|
+
ext_headers=ext_headers,
|
|
106
|
+
chunk_size=1024 * 1024,
|
|
107
|
+
)
|
|
102
108
|
|
|
103
109
|
@auto_task
|
|
104
110
|
async def download_audio(
|
|
@@ -111,7 +117,12 @@ class StreamDownloader:
|
|
|
111
117
|
"""download audio file by url with stream"""
|
|
112
118
|
if audio_name is None:
|
|
113
119
|
audio_name = generate_file_name(url, ".mp3")
|
|
114
|
-
|
|
120
|
+
|
|
121
|
+
return await self.download_file(
|
|
122
|
+
url,
|
|
123
|
+
file_name=audio_name,
|
|
124
|
+
ext_headers=ext_headers,
|
|
125
|
+
)
|
|
115
126
|
|
|
116
127
|
@auto_task
|
|
117
128
|
async def download_img(
|
|
@@ -124,7 +135,12 @@ class StreamDownloader:
|
|
|
124
135
|
"""download image file by url with stream"""
|
|
125
136
|
if img_name is None:
|
|
126
137
|
img_name = generate_file_name(url, ".jpg")
|
|
127
|
-
|
|
138
|
+
|
|
139
|
+
return await self.download_file(
|
|
140
|
+
url,
|
|
141
|
+
file_name=img_name,
|
|
142
|
+
ext_headers=ext_headers,
|
|
143
|
+
)
|
|
128
144
|
|
|
129
145
|
@auto_task
|
|
130
146
|
async def download_av_and_merge(
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from typing import Any, TypeVar, ParamSpec
|
|
2
|
+
from asyncio import Task, create_task
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from collections.abc import Callable, Coroutine
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PathTask:
|
|
9
|
+
def __init__(self, task: Task[Path]):
|
|
10
|
+
self._task: Task[Path] = task
|
|
11
|
+
self._path: Path | None = None
|
|
12
|
+
|
|
13
|
+
async def get(self) -> Path:
|
|
14
|
+
if self._path is not None:
|
|
15
|
+
return self._path
|
|
16
|
+
|
|
17
|
+
self._path = await self._task
|
|
18
|
+
return self._path
|
|
19
|
+
|
|
20
|
+
def __await__(self):
|
|
21
|
+
return self.get().__await__()
|
|
22
|
+
|
|
23
|
+
async def safe_get(
|
|
24
|
+
self,
|
|
25
|
+
on_error: Callable[[Exception], None] | None = None,
|
|
26
|
+
) -> Path | None:
|
|
27
|
+
"""任务失败, 返回 None"""
|
|
28
|
+
try:
|
|
29
|
+
return await self.get()
|
|
30
|
+
except Exception as e:
|
|
31
|
+
if on_error is not None:
|
|
32
|
+
on_error(e)
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def uri(self) -> str | None:
|
|
37
|
+
return self._path.as_uri() if self._path is not None else None
|
|
38
|
+
|
|
39
|
+
def __repr__(self) -> str:
|
|
40
|
+
if self._path is not None:
|
|
41
|
+
return f"PathTask(path={self._path.name})"
|
|
42
|
+
else:
|
|
43
|
+
return f"PathTask(task={self._task.get_name()}, done={self._task.done()})"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class OptionalPathTask:
|
|
47
|
+
"""封装可选的 PathTask, 提供便捷的 API 避免频繁判空"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, path_task: PathTask | None = None):
|
|
50
|
+
self._path_task: PathTask | None = path_task
|
|
51
|
+
|
|
52
|
+
async def get(self) -> Path | None:
|
|
53
|
+
if self._path_task is None:
|
|
54
|
+
return None
|
|
55
|
+
return await self._path_task.get()
|
|
56
|
+
|
|
57
|
+
def __await__(self):
|
|
58
|
+
return self.get().__await__()
|
|
59
|
+
|
|
60
|
+
async def safe_get(
|
|
61
|
+
self,
|
|
62
|
+
on_error: Callable[[Exception], None] | None = None,
|
|
63
|
+
) -> Path | None:
|
|
64
|
+
"""任务失败, 返回 None"""
|
|
65
|
+
if self._path_task is None:
|
|
66
|
+
return None
|
|
67
|
+
return await self._path_task.safe_get(on_error)
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def uri(self) -> str | None:
|
|
71
|
+
if self._path_task is None:
|
|
72
|
+
return None
|
|
73
|
+
return self._path_task.uri
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
return f"{self._path_task}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
P = ParamSpec("P")
|
|
80
|
+
T = TypeVar("T")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def auto_task(func: Callable[P, Coroutine[Any, Any, Path]]) -> Callable[P, PathTask]:
|
|
84
|
+
"""装饰器:自动将异步函数调用转换为 Task, 完整保留类型提示"""
|
|
85
|
+
|
|
86
|
+
@wraps(func)
|
|
87
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> PathTask:
|
|
88
|
+
coro = func(*args, **kwargs)
|
|
89
|
+
name = " | ".join(str(arg) for arg in args if isinstance(arg, str))
|
|
90
|
+
return PathTask(create_task(coro, name=func.__name__ + " | " + name))
|
|
91
|
+
|
|
92
|
+
return wrapper
|
|
@@ -78,8 +78,8 @@ async def parser_handler(
|
|
|
78
78
|
logger.debug(f"命中缓存: {cache_key}, 结果: {result}")
|
|
79
79
|
|
|
80
80
|
# 3. 渲染内容消息并发送
|
|
81
|
-
renderer = get_renderer(result.platform.name)
|
|
82
|
-
async for message in renderer.render_messages(
|
|
81
|
+
renderer = get_renderer(result.platform.name)(result)
|
|
82
|
+
async for message in renderer.render_messages():
|
|
83
83
|
await message.send()
|
|
84
84
|
|
|
85
85
|
# 4. 缓存解析结果
|
|
@@ -40,14 +40,17 @@ class AcfunParser(BaseParser):
|
|
|
40
40
|
video_name=f"acfun_{acid}.mp4",
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
-
video_content = self.create_video_content(
|
|
43
|
+
video_content = self.create_video_content(
|
|
44
|
+
video_task,
|
|
45
|
+
cover_url=video_info.coverUrl,
|
|
46
|
+
)
|
|
44
47
|
|
|
45
48
|
return self.result(
|
|
46
49
|
title=video_info.title,
|
|
47
50
|
text=video_info.text,
|
|
48
51
|
author=author,
|
|
49
52
|
timestamp=video_info.timestamp,
|
|
50
|
-
|
|
53
|
+
video=video_content,
|
|
51
54
|
)
|
|
52
55
|
|
|
53
56
|
async def parse_video_info(self, url: str):
|
|
@@ -15,6 +15,7 @@ from ..constants import PlatformEnum as PlatformEnum
|
|
|
15
15
|
from ..exception import ParseException
|
|
16
16
|
from ..exception import IgnoreException as IgnoreException
|
|
17
17
|
from ..exception import DownloadException as DownloadException
|
|
18
|
+
from ..download.task import PathTask, OptionalPathTask
|
|
18
19
|
|
|
19
20
|
T = TypeVar("T", bound="BaseParser")
|
|
20
21
|
HandlerFunc = Callable[[T, Match[str]], Coroutine[Any, Any, ParseResult]]
|
|
@@ -169,13 +170,13 @@ class BaseParser:
|
|
|
169
170
|
avatar_task = None
|
|
170
171
|
if avatar_url:
|
|
171
172
|
avatar_task = DOWNLOADER.download_img(avatar_url, ext_headers=self.headers)
|
|
172
|
-
return Author(name=name, avatar=avatar_task, description=description)
|
|
173
|
+
return Author(name=name, avatar=OptionalPathTask(avatar_task), description=description)
|
|
173
174
|
|
|
174
175
|
def create_video_content(
|
|
175
176
|
self,
|
|
176
|
-
url_or_task: str | Task[Path],
|
|
177
|
+
url_or_task: str | Task[Path] | PathTask,
|
|
177
178
|
cover_url: str | None = None,
|
|
178
|
-
duration: float =
|
|
179
|
+
duration: float | None = None,
|
|
179
180
|
):
|
|
180
181
|
"""创建视频内容"""
|
|
181
182
|
from .data import VideoContent
|
|
@@ -183,10 +184,15 @@ class BaseParser:
|
|
|
183
184
|
cover_task = None
|
|
184
185
|
if cover_url:
|
|
185
186
|
cover_task = DOWNLOADER.download_img(cover_url, ext_headers=self.headers)
|
|
187
|
+
|
|
186
188
|
if isinstance(url_or_task, str):
|
|
187
|
-
|
|
189
|
+
path_task = DOWNLOADER.download_video(url_or_task, ext_headers=self.headers)
|
|
190
|
+
elif isinstance(url_or_task, Task):
|
|
191
|
+
path_task = PathTask(url_or_task)
|
|
192
|
+
elif isinstance(url_or_task, PathTask):
|
|
193
|
+
path_task = url_or_task
|
|
188
194
|
|
|
189
|
-
return VideoContent(
|
|
195
|
+
return VideoContent(path_task, OptionalPathTask(cover_task), duration)
|
|
190
196
|
|
|
191
197
|
def create_image_contents(
|
|
192
198
|
self,
|
|
@@ -201,14 +207,18 @@ class BaseParser:
|
|
|
201
207
|
|
|
202
208
|
def create_image_content(
|
|
203
209
|
self,
|
|
204
|
-
url_or_task: str | Task[Path],
|
|
210
|
+
url_or_task: str | Task[Path] | PathTask,
|
|
205
211
|
alt: str | None = None,
|
|
206
212
|
):
|
|
207
213
|
"""创建图片内容"""
|
|
208
214
|
if isinstance(url_or_task, str):
|
|
209
|
-
|
|
215
|
+
path_task = DOWNLOADER.download_img(url_or_task, ext_headers=self.headers)
|
|
216
|
+
elif isinstance(url_or_task, Task):
|
|
217
|
+
path_task = PathTask(url_or_task)
|
|
218
|
+
elif isinstance(url_or_task, PathTask):
|
|
219
|
+
path_task = url_or_task
|
|
210
220
|
|
|
211
|
-
return ImageContent(
|
|
221
|
+
return ImageContent(path_task, alt=alt)
|
|
212
222
|
|
|
213
223
|
def create_dynamic_contents(
|
|
214
224
|
self,
|
|
@@ -223,22 +233,38 @@ class BaseParser:
|
|
|
223
233
|
contents.append(DynamicContent(task))
|
|
224
234
|
return contents
|
|
225
235
|
|
|
236
|
+
def create_dynamic_content(
|
|
237
|
+
self,
|
|
238
|
+
url_or_task: str | Task[Path] | PathTask,
|
|
239
|
+
):
|
|
240
|
+
"""创建动态图片内容"""
|
|
241
|
+
from .data import DynamicContent
|
|
242
|
+
|
|
243
|
+
if isinstance(url_or_task, str):
|
|
244
|
+
path_task = DOWNLOADER.download_video(url_or_task, ext_headers=self.headers)
|
|
245
|
+
elif isinstance(url_or_task, Task):
|
|
246
|
+
path_task = PathTask(url_or_task)
|
|
247
|
+
elif isinstance(url_or_task, PathTask):
|
|
248
|
+
path_task = url_or_task
|
|
249
|
+
|
|
250
|
+
return DynamicContent(path_task)
|
|
251
|
+
|
|
226
252
|
def create_audio_content(
|
|
227
253
|
self,
|
|
228
|
-
url_or_task: str | Task[Path],
|
|
254
|
+
url_or_task: str | Task[Path] | PathTask,
|
|
229
255
|
duration: float = 0.0,
|
|
230
256
|
):
|
|
231
257
|
"""创建音频内容"""
|
|
232
258
|
from .data import AudioContent
|
|
233
259
|
|
|
234
260
|
if isinstance(url_or_task, str):
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
261
|
+
path_task = DOWNLOADER.download_audio(url_or_task, ext_headers=self.headers)
|
|
262
|
+
elif isinstance(url_or_task, Task):
|
|
263
|
+
path_task = PathTask(url_or_task)
|
|
264
|
+
elif isinstance(url_or_task, PathTask):
|
|
265
|
+
path_task = url_or_task
|
|
238
266
|
|
|
239
|
-
|
|
240
|
-
"""创建空的图片内容列表"""
|
|
241
|
-
return []
|
|
267
|
+
return AudioContent(path_task, duration)
|
|
242
268
|
|
|
243
269
|
@property
|
|
244
270
|
def downloader(self):
|
|
@@ -134,15 +134,22 @@ class BilibiliParser(BaseParser):
|
|
|
134
134
|
logger.warning(f"视频时长 {page_info.duration} 秒, 超过 {pconfig.duration_maximum} 秒, 取消下载")
|
|
135
135
|
raise IgnoreException
|
|
136
136
|
if a_url is not None:
|
|
137
|
-
|
|
138
|
-
v_url,
|
|
137
|
+
path = await self.downloader.download_av_and_merge(
|
|
138
|
+
v_url,
|
|
139
|
+
a_url,
|
|
140
|
+
output_path=output_path,
|
|
141
|
+
ext_headers=self.headers,
|
|
139
142
|
)
|
|
140
143
|
else:
|
|
141
|
-
|
|
144
|
+
path = await self.downloader.download_file(
|
|
145
|
+
v_url,
|
|
146
|
+
file_name=output_path.name,
|
|
147
|
+
ext_headers=self.headers,
|
|
148
|
+
)
|
|
149
|
+
return path
|
|
142
150
|
|
|
143
|
-
video_task = asyncio.create_task(download_video())
|
|
144
151
|
video_content = self.create_video_content(
|
|
145
|
-
|
|
152
|
+
asyncio.create_task(download_video()),
|
|
146
153
|
page_info.cover,
|
|
147
154
|
page_info.duration,
|
|
148
155
|
)
|
|
@@ -153,7 +160,7 @@ class BilibiliParser(BaseParser):
|
|
|
153
160
|
timestamp=page_info.timestamp,
|
|
154
161
|
text=video_info.desc,
|
|
155
162
|
author=author,
|
|
156
|
-
|
|
163
|
+
video=video_content,
|
|
157
164
|
extra={"info": ai_summary},
|
|
158
165
|
)
|
|
159
166
|
|
|
@@ -210,25 +217,26 @@ class BilibiliParser(BaseParser):
|
|
|
210
217
|
opus_info = await bili_opus.get_info()
|
|
211
218
|
if not isinstance(opus_info, dict):
|
|
212
219
|
raise ParseException("获取图文动态信息失败")
|
|
220
|
+
|
|
213
221
|
# 转换为结构体
|
|
214
222
|
opus_data = convert(opus_info, OpusItem)
|
|
215
223
|
logger.debug(f"opus_data: {opus_data}")
|
|
216
224
|
author = self.create_author(*opus_data.name_avatar)
|
|
217
225
|
|
|
218
226
|
# 按顺序处理图文内容
|
|
219
|
-
|
|
227
|
+
result = self.result(
|
|
228
|
+
author=author,
|
|
229
|
+
title=opus_data.title,
|
|
230
|
+
timestamp=opus_data.timestamp,
|
|
231
|
+
)
|
|
232
|
+
|
|
220
233
|
for node in opus_data.extract_nodes():
|
|
221
234
|
if isinstance(node, str):
|
|
222
|
-
graphics.append(node)
|
|
235
|
+
result.graphics.append(node)
|
|
223
236
|
else:
|
|
224
|
-
graphics.append(self.create_image_content(node.url, alt=node.alt))
|
|
237
|
+
result.graphics.append(self.create_image_content(node.url, alt=node.alt))
|
|
225
238
|
|
|
226
|
-
return
|
|
227
|
-
title=opus_data.title,
|
|
228
|
-
author=author,
|
|
229
|
-
timestamp=opus_data.timestamp,
|
|
230
|
-
graphics=graphics,
|
|
231
|
-
)
|
|
239
|
+
return result
|
|
232
240
|
|
|
233
241
|
async def parse_live(self, room_id: int):
|
|
234
242
|
"""解析直播"""
|