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.
Files changed (68) hide show
  1. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/PKG-INFO +3 -3
  2. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/README.md +1 -1
  3. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/pyproject.toml +6 -6
  4. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/download/__init__.py +19 -3
  5. nonebot_plugin_parser-2.5.2/src/nonebot_plugin_parser/download/task.py +92 -0
  6. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/__init__.py +2 -2
  7. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/acfun/__init__.py +5 -2
  8. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/base.py +41 -15
  9. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +23 -15
  10. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/data.py +55 -91
  11. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/__init__.py +26 -26
  12. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/kuaishou/__init__.py +13 -13
  13. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/nga.py +11 -19
  14. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/tiktok.py +8 -4
  15. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/twitter.py +29 -41
  16. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/__init__.py +20 -18
  17. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/__init__.py +24 -25
  18. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/youtube/__init__.py +11 -32
  19. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/__init__.py +6 -11
  20. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/base.py +48 -35
  21. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/common.py +292 -211
  22. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/default.py +9 -12
  23. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/htmlrender.py +9 -6
  24. nonebot_plugin_parser-2.5.2/src/nonebot_plugin_parser/renders/templates/card.html.jinja2 +668 -0
  25. nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/download/task.py +0 -19
  26. nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/renders/templates/card.html.jinja +0 -606
  27. nonebot_plugin_parser-2.5.0/src/nonebot_plugin_parser/renders/weibo.py +0 -18
  28. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/__init__.py +0 -0
  29. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/config.py +0 -0
  30. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/constants.py +0 -0
  31. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
  32. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/exception.py +0 -0
  33. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/helper.py +0 -0
  34. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/filter.py +0 -0
  35. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/matchers/rule.py +0 -0
  36. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/__init__.py +0 -0
  37. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/acfun/video.py +0 -0
  38. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
  39. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
  40. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
  41. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
  42. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
  43. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
  44. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
  45. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
  46. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/slides.py +0 -0
  47. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/douyin/video.py +0 -0
  48. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/kuaishou/states.py +0 -0
  49. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/article.py +0 -0
  50. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/common.py +0 -0
  51. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/weibo/show.py +0 -0
  52. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/common.py +0 -0
  53. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/discovery.py +0 -0
  54. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/explore.py +0 -0
  55. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/parsers/youtube/meta.py +0 -0
  56. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW.ttf +0 -0
  57. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/__init__.py +0 -0
  58. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/avatar.png +0 -0
  59. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
  60. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
  61. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
  62. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/play.png +0 -0
  63. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
  64. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
  65. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
  66. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
  67. {nonebot_plugin_parser-2.5.0 → nonebot_plugin_parser-2.5.2}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
  68. {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.0
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.2.21 ; extra == 'all'
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
 
@@ -145,7 +145,7 @@
145
145
 
146
146
  uv add "nonebot-plugin-parser[htmlkit]"
147
147
 
148
- `htmlrender`, 使用 `playwright` 渲染 `html`, 插件现有模版有点问题,并且极其丑陋,不建议使用
148
+ `htmlrender`, 使用 `playwright` 渲染 `html`, 插件自 `v2.5.0` 起已正式支持
149
149
 
150
150
  uv add "nonebot-plugin-parser[htmlrender]"
151
151
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nonebot-plugin-parser"
3
- version = "2.5.0"
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.2.21",
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.0,<1.0.0",
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.2.21",
88
- "types-yt-dlp>=2026.2.21.20260223",
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.0"
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
- return await self.download_file(url, file_name=video_name, ext_headers=ext_headers, chunk_size=1024 * 1024)
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
- return await self.download_file(url, file_name=audio_name, ext_headers=ext_headers)
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
- return await self.download_file(url, file_name=img_name, ext_headers=ext_headers)
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(result):
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(video_task, cover_url=video_info.coverUrl)
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
- contents=[video_content],
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 = 0.0,
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
- url_or_task = DOWNLOADER.download_video(url_or_task, ext_headers=self.headers)
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(url_or_task, cover_task, duration)
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
- url_or_task = DOWNLOADER.download_img(url_or_task, ext_headers=self.headers)
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(url_or_task, alt=alt)
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
- url_or_task = DOWNLOADER.download_audio(url_or_task, ext_headers=self.headers)
236
-
237
- return AudioContent(url_or_task, duration)
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
- def create_empty_graphics(self) -> list[str | ImageContent]:
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
- return await self.downloader.download_av_and_merge(
138
- v_url, a_url, output_path=output_path, ext_headers=self.headers
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
- return await self.downloader.download_file(v_url, file_name=output_path.name, ext_headers=self.headers)
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
- video_task,
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
- contents=[video_content],
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
- graphics = self.create_empty_graphics()
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 self.result(
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
  """解析直播"""