parsehub 2.1.2__tar.gz → 2.1.4__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.
- {parsehub-2.1.2/src/parsehub.egg-info → parsehub-2.1.4}/PKG-INFO +2 -2
- {parsehub-2.1.2 → parsehub-2.1.4}/pyproject.toml +3 -2
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/base/ytdlp.py +2 -2
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/bilibili.py +6 -5
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/douyin.py +28 -14
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/bilibili.py +4 -4
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/coolapk.py +4 -4
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/douyin.py +1 -1
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/instagram.py +5 -5
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/twitter.py +1 -1
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/zhihu.py +4 -4
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/result.py +13 -13
- {parsehub-2.1.2 → parsehub-2.1.4/src/parsehub.egg-info}/PKG-INFO +2 -2
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub.egg-info/requires.txt +1 -1
- {parsehub-2.1.2 → parsehub-2.1.4}/test/test_core_offline.py +1 -2
- {parsehub-2.1.2 → parsehub-2.1.4}/LICENSE +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/README.md +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/setup.cfg +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/cli.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/cli_config.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/config/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/config/config.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/errors.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/base/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/base/base.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/coolapk.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/facebook.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/instagram.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/kuaishou.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/pipix.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/snapchat.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/threads.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/tieba.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/tiktok.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/twitter.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/weibo.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/weixin.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/xhs.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/xiaoheihe.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/youtube.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/zhihu.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/parsers/parser/zuiyou.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/kuaishou.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/pipix.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/threads.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/tieba.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/tiktok.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/weibo.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/weixin.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/xhs.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/xiaoheihe.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/provider_api/zuiyou.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/__init__.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/callback.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/media_file.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/media_ref.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/platform.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/types/post.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/utils/downloader.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/utils/helpers.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub/utils/media_info.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub.egg-info/SOURCES.txt +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub.egg-info/dependency_links.txt +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub.egg-info/entry_points.txt +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/src/parsehub.egg-info/top_level.txt +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/test/test_cli.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/test/test_cli_config.py +0 -0
- {parsehub-2.1.2 → parsehub-2.1.4}/test/test_downloader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parsehub
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: 轻量、异步、开箱即用的社交媒体聚合解析库
|
|
5
5
|
Author-email: 梓澪 <zilingmio@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -34,7 +34,7 @@ Requires-Dist: python-slugify[unidecode]>=8.0.4
|
|
|
34
34
|
Requires-Dist: opencv-python-headless>=4.13.0.92
|
|
35
35
|
Requires-Dist: cryptography>=49.0.0
|
|
36
36
|
Requires-Dist: gmssl>=3.2.2
|
|
37
|
-
Requires-Dist:
|
|
37
|
+
Requires-Dist: signerpy>=0.12.0
|
|
38
38
|
Provides-Extra: cli
|
|
39
39
|
Requires-Dist: argcomplete>=3.6.3; extra == "cli"
|
|
40
40
|
Requires-Dist: platformdirs>=4.5.1; extra == "cli"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "parsehub"
|
|
3
|
-
version = "2.1.
|
|
3
|
+
version = "2.1.4"
|
|
4
4
|
description = "轻量、异步、开箱即用的社交媒体聚合解析库"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12.0"
|
|
@@ -37,7 +37,7 @@ dependencies = [
|
|
|
37
37
|
"opencv-python-headless>=4.13.0.92",
|
|
38
38
|
"cryptography>=49.0.0",
|
|
39
39
|
"gmssl>=3.2.2",
|
|
40
|
-
"
|
|
40
|
+
"signerpy>=0.12.0",
|
|
41
41
|
]
|
|
42
42
|
|
|
43
43
|
[project.scripts]
|
|
@@ -66,6 +66,7 @@ Issues = "https://github.com/z-mio/parsehub/issues"
|
|
|
66
66
|
|
|
67
67
|
[tool.ruff]
|
|
68
68
|
line-length = 120
|
|
69
|
+
exclude = ["test/_*.py"]
|
|
69
70
|
|
|
70
71
|
[tool.ruff.lint]
|
|
71
72
|
select = [
|
|
@@ -445,9 +445,9 @@ class YtVideoParseResult(VideoParseResult):
|
|
|
445
445
|
def __init__(
|
|
446
446
|
self,
|
|
447
447
|
dl: "YtVideoInfo",
|
|
448
|
-
title: str
|
|
448
|
+
title: str = "",
|
|
449
449
|
video: VideoRef | None = None,
|
|
450
|
-
content: str
|
|
450
|
+
content: str = "",
|
|
451
451
|
):
|
|
452
452
|
"""dl: yt-dlp解析结果"""
|
|
453
453
|
self.dl = dl
|
|
@@ -106,7 +106,6 @@ class BiliParse(BaseParser):
|
|
|
106
106
|
view = data["View"]
|
|
107
107
|
|
|
108
108
|
cid = view["cid"]
|
|
109
|
-
part = ""
|
|
110
109
|
duration = view["duration"]
|
|
111
110
|
dimension = view["dimension"]
|
|
112
111
|
desc = view["desc"]
|
|
@@ -114,7 +113,6 @@ class BiliParse(BaseParser):
|
|
|
114
113
|
if p != 1 and (pages := view.get("pages")):
|
|
115
114
|
if page_info := next((i for i in pages if i["page"] == p), None):
|
|
116
115
|
cid = page_info["cid"]
|
|
117
|
-
part = page_info["part"]
|
|
118
116
|
duration = page_info["duration"]
|
|
119
117
|
dimension = page_info["dimension"]
|
|
120
118
|
|
|
@@ -123,9 +121,12 @@ class BiliParse(BaseParser):
|
|
|
123
121
|
|
|
124
122
|
durl = video_playurl["data"]["durl"][0]
|
|
125
123
|
video_url = self.change_source(durl["backup_url"][0]) if durl.get("backup_url") else durl["url"]
|
|
124
|
+
content = desc.strip()
|
|
125
|
+
if content == "-":
|
|
126
|
+
content = ""
|
|
126
127
|
return BiliVideoParseResult(
|
|
127
128
|
title=data["View"]["title"],
|
|
128
|
-
content=
|
|
129
|
+
content=content,
|
|
129
130
|
video=VideoRef(
|
|
130
131
|
url=video_url,
|
|
131
132
|
thumb_url=data["View"]["pic"],
|
|
@@ -147,9 +148,9 @@ class BiliParse(BaseParser):
|
|
|
147
148
|
)
|
|
148
149
|
|
|
149
150
|
@staticmethod
|
|
150
|
-
def hashtag_handler(desc: str
|
|
151
|
+
def hashtag_handler(desc: str) -> str:
|
|
151
152
|
if not desc:
|
|
152
|
-
return
|
|
153
|
+
return ""
|
|
153
154
|
hashtags = re.findall(r" ?#[^#]+# ?", desc)
|
|
154
155
|
for hashtag in hashtags:
|
|
155
156
|
desc = desc.replace(hashtag, f" {hashtag.strip().removesuffix('#')} ")
|
|
@@ -117,13 +117,18 @@ def remove_video_watermark(url: str) -> str:
|
|
|
117
117
|
return url.replace("playwm", "play")
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
def
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
def _p_play_addr(video_data: dict) -> tuple[str | None, int, int]:
|
|
121
|
+
play_addr = video_data["play_addr"]
|
|
122
|
+
url_list = play_addr["url_list"]
|
|
123
|
+
if not url_list:
|
|
124
|
+
return None, 0, 0
|
|
125
|
+
return url_list[0], play_addr.get("width", 0), play_addr.get("height", 0)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _p_bit_rates(video_data: dict, filter_bytevc1: bool = False) -> tuple[str, int, int]:
|
|
129
|
+
bit_rates: list = video_data.get("bit_rate", [])
|
|
130
|
+
if filter_bytevc1:
|
|
131
|
+
bit_rates = list(filter(lambda i: i.get("is_bytevc1", 0) == 0, bit_rates))
|
|
127
132
|
bit_rates.sort(
|
|
128
133
|
key=lambda x: (
|
|
129
134
|
x.get("play_addr", {}).get("width", 0) * x.get("play_addr", {}).get("height", 0),
|
|
@@ -135,11 +140,20 @@ def parse_video_info(video_data: dict) -> dict:
|
|
|
135
140
|
best_quality = bit_rates[0]
|
|
136
141
|
|
|
137
142
|
play_addr = best_quality.get("play_addr", {})
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
url_list = play_addr.get("url_list", [])
|
|
144
|
+
return url_list[0], play_addr.get("width", 0), play_addr.get("height", 0)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def parse_video_info(video_data: dict) -> dict:
|
|
148
|
+
is_app = video_data.get("is_bytevc1", None) is not None # 只有 app 接口带 is_bytevc1 参数
|
|
149
|
+
if is_app:
|
|
150
|
+
u, w, h = _p_play_addr(video_data)
|
|
151
|
+
if not u:
|
|
152
|
+
u, w, h = _p_bit_rates(video_data, True)
|
|
153
|
+
else:
|
|
154
|
+
u, w, h = _p_bit_rates(video_data)
|
|
141
155
|
|
|
142
|
-
video_url = remove_video_watermark(
|
|
156
|
+
video_url = remove_video_watermark(u)
|
|
143
157
|
|
|
144
158
|
cover = video_data.get("cover", {})
|
|
145
159
|
thumb_url_list = cover.get("url_list", [])
|
|
@@ -148,9 +162,9 @@ def parse_video_info(video_data: dict) -> dict:
|
|
|
148
162
|
return {
|
|
149
163
|
"video_url": video_url,
|
|
150
164
|
"thumb_url": thumb_url,
|
|
151
|
-
"duration":
|
|
152
|
-
"width":
|
|
153
|
-
"height":
|
|
165
|
+
"duration": video_data.get("duration", 0),
|
|
166
|
+
"width": w,
|
|
167
|
+
"height": h,
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
|
|
@@ -245,8 +245,8 @@ class BiliImage:
|
|
|
245
245
|
|
|
246
246
|
@dataclass(kw_only=True)
|
|
247
247
|
class BiliDynamic:
|
|
248
|
-
title: str
|
|
249
|
-
content: str
|
|
248
|
+
title: str = ""
|
|
249
|
+
content: str = ""
|
|
250
250
|
images: list[BiliImage] | None = None
|
|
251
251
|
|
|
252
252
|
@classmethod
|
|
@@ -361,10 +361,10 @@ class BiliDynamic:
|
|
|
361
361
|
return cls(title=title)
|
|
362
362
|
|
|
363
363
|
@staticmethod
|
|
364
|
-
def _get_desc_text(module_dynamic: dict) -> str
|
|
364
|
+
def _get_desc_text(module_dynamic: dict) -> str:
|
|
365
365
|
if desc := module_dynamic["desc"]:
|
|
366
366
|
return str(desc["text"]).strip()
|
|
367
|
-
return
|
|
367
|
+
return ""
|
|
368
368
|
|
|
369
369
|
@staticmethod
|
|
370
370
|
def _get_major_cover(major_content: dict) -> list[BiliImage] | None:
|
|
@@ -10,9 +10,9 @@ from ..utils.helpers import UA
|
|
|
10
10
|
|
|
11
11
|
@dataclass
|
|
12
12
|
class Coolapk:
|
|
13
|
-
title: str
|
|
14
|
-
markdown_content: str
|
|
15
|
-
text_content: str
|
|
13
|
+
title: str = ""
|
|
14
|
+
markdown_content: str = ""
|
|
15
|
+
text_content: str = ""
|
|
16
16
|
imgs: list[str] | None = None
|
|
17
17
|
|
|
18
18
|
@classmethod
|
|
@@ -37,6 +37,6 @@ class Coolapk:
|
|
|
37
37
|
if feed_element and (feed_content := feed_element.text.strip()):
|
|
38
38
|
message_image_group = soup.find(class_="message-image-group")
|
|
39
39
|
imgs = [f"https:{i['src']}" for i in message_image_group.find_all("img")] if message_image_group else []
|
|
40
|
-
return cls(
|
|
40
|
+
return cls("", "", feed_content, imgs)
|
|
41
41
|
|
|
42
42
|
raise ValueError("获取内容失败, 分享时请保留 shareKey 或 s 参数")
|
|
@@ -1147,7 +1147,7 @@ class DouyinMobileCrawler:
|
|
|
1147
1147
|
return cast(dict[str, Any], payload)
|
|
1148
1148
|
last_error = f"{host} missing aweme_detail: {payload.get('status_msg') or payload}"
|
|
1149
1149
|
await asyncio.sleep(0.15)
|
|
1150
|
-
raise ParseError(f"
|
|
1150
|
+
raise ParseError(f"获取抖音作品失败: {last_error}")
|
|
1151
1151
|
|
|
1152
1152
|
async def parse(self, raw_url: str) -> dict:
|
|
1153
1153
|
aweme_id = await self.get_aweme_id(raw_url)
|
|
@@ -65,17 +65,17 @@ class InstagramPost:
|
|
|
65
65
|
return bool(self._field("is_video"))
|
|
66
66
|
|
|
67
67
|
@property
|
|
68
|
-
def title(self) -> str
|
|
69
|
-
return self._node.get("title")
|
|
68
|
+
def title(self) -> str:
|
|
69
|
+
return str(self._node.get("title", ""))
|
|
70
70
|
|
|
71
71
|
@property
|
|
72
|
-
def caption(self) -> str
|
|
72
|
+
def caption(self) -> str:
|
|
73
73
|
caption_edges = self._node.get("edge_media_to_caption", {}).get("edges") or []
|
|
74
74
|
if caption_edges:
|
|
75
75
|
if text := caption_edges[0].get("node", {}).get("text"):
|
|
76
76
|
return str(text)
|
|
77
|
-
return
|
|
78
|
-
return self._node.get("caption")
|
|
77
|
+
return ""
|
|
78
|
+
return str(self._node.get("caption", ""))
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
81
|
def url(self) -> str:
|
|
@@ -91,8 +91,8 @@ class ZhihuQA:
|
|
|
91
91
|
class ZhihuZhuanLan:
|
|
92
92
|
title: str
|
|
93
93
|
imgs: list[str]
|
|
94
|
-
markdown_content: str
|
|
95
|
-
plaintext_content: str
|
|
94
|
+
markdown_content: str = ""
|
|
95
|
+
plaintext_content: str = ""
|
|
96
96
|
|
|
97
97
|
@classmethod
|
|
98
98
|
def parse(cls, data: dict) -> Self:
|
|
@@ -122,8 +122,8 @@ class ZhihuPin:
|
|
|
122
122
|
type: ZhihuPinType
|
|
123
123
|
title: str
|
|
124
124
|
media: list[ZhihuMedia]
|
|
125
|
-
markdown_content: str
|
|
126
|
-
plaintext_content: str
|
|
125
|
+
markdown_content: str = ""
|
|
126
|
+
plaintext_content: str = ""
|
|
127
127
|
|
|
128
128
|
@classmethod
|
|
129
129
|
def parse(cls, result: dict) -> "ZhihuPin":
|
|
@@ -30,8 +30,8 @@ class ParseResult(ABC): # noqa: B024
|
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
32
|
self,
|
|
33
|
-
title: str
|
|
34
|
-
content: str
|
|
33
|
+
title: str = "",
|
|
34
|
+
content: str = "",
|
|
35
35
|
media: Sequence[AnyMediaRef] | AnyMediaRef | None = None,
|
|
36
36
|
platform: Platform | None = None,
|
|
37
37
|
):
|
|
@@ -42,8 +42,8 @@ class ParseResult(ABC): # noqa: B024
|
|
|
42
42
|
:param platform: 平台
|
|
43
43
|
"""
|
|
44
44
|
self.raw_url: str = ""
|
|
45
|
-
self.title =
|
|
46
|
-
self.content =
|
|
45
|
+
self.title = title.strip()
|
|
46
|
+
self.content = content.strip()
|
|
47
47
|
self.media = media
|
|
48
48
|
self.platform = platform
|
|
49
49
|
self.name = slugify(
|
|
@@ -294,9 +294,9 @@ class VideoParseResult(ParseResult):
|
|
|
294
294
|
|
|
295
295
|
def __init__(
|
|
296
296
|
self,
|
|
297
|
-
title: str
|
|
297
|
+
title: str = "",
|
|
298
298
|
video: str | VideoRef | None = None,
|
|
299
|
-
content: str
|
|
299
|
+
content: str = "",
|
|
300
300
|
):
|
|
301
301
|
video = VideoRef(url=video) if isinstance(video, str) else video
|
|
302
302
|
super().__init__(
|
|
@@ -313,9 +313,9 @@ class ImageParseResult(ParseResult):
|
|
|
313
313
|
|
|
314
314
|
def __init__(
|
|
315
315
|
self,
|
|
316
|
-
title: str
|
|
316
|
+
title: str = "",
|
|
317
317
|
photo: Sequence[str | ImageRef | AniRef | LivePhotoRef] | None = None,
|
|
318
|
-
content: str
|
|
318
|
+
content: str = "",
|
|
319
319
|
):
|
|
320
320
|
media = [ImageRef(url=p) if isinstance(p, str) else p for p in photo] if photo else None
|
|
321
321
|
super().__init__(title=title, media=media, content=content)
|
|
@@ -328,9 +328,9 @@ class MultimediaParseResult(ParseResult):
|
|
|
328
328
|
|
|
329
329
|
def __init__(
|
|
330
330
|
self,
|
|
331
|
-
title: str
|
|
331
|
+
title: str = "",
|
|
332
332
|
media: Sequence[AnyMediaRef] | None = None,
|
|
333
|
-
content: str
|
|
333
|
+
content: str = "",
|
|
334
334
|
):
|
|
335
335
|
super().__init__(title=title, media=media, content=content)
|
|
336
336
|
|
|
@@ -342,16 +342,16 @@ class RichTextParseResult(ParseResult):
|
|
|
342
342
|
|
|
343
343
|
def __init__(
|
|
344
344
|
self,
|
|
345
|
-
title: str
|
|
345
|
+
title: str = "",
|
|
346
346
|
media: Sequence[AnyMediaRef] | None = None,
|
|
347
|
-
markdown_content: str
|
|
347
|
+
markdown_content: str = "",
|
|
348
348
|
):
|
|
349
349
|
"""
|
|
350
350
|
:param title: 标题
|
|
351
351
|
:param media: 文章中的媒体
|
|
352
352
|
:param markdown_content: markdown 格式正文
|
|
353
353
|
"""
|
|
354
|
-
self.markdown_content = markdown_content
|
|
354
|
+
self.markdown_content = markdown_content
|
|
355
355
|
super().__init__(title=title, media=media, content=self.plaintext_content)
|
|
356
356
|
|
|
357
357
|
def __repr__(self) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parsehub
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: 轻量、异步、开箱即用的社交媒体聚合解析库
|
|
5
5
|
Author-email: 梓澪 <zilingmio@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -34,7 +34,7 @@ Requires-Dist: python-slugify[unidecode]>=8.0.4
|
|
|
34
34
|
Requires-Dist: opencv-python-headless>=4.13.0.92
|
|
35
35
|
Requires-Dist: cryptography>=49.0.0
|
|
36
36
|
Requires-Dist: gmssl>=3.2.2
|
|
37
|
-
Requires-Dist:
|
|
37
|
+
Requires-Dist: signerpy>=0.12.0
|
|
38
38
|
Provides-Extra: cli
|
|
39
39
|
Requires-Dist: argcomplete>=3.6.3; extra == "cli"
|
|
40
40
|
Requires-Dist: platformdirs>=4.5.1; extra == "cli"
|
|
@@ -265,11 +265,11 @@ class TestDouyinStorySupport(unittest.TestCase):
|
|
|
265
265
|
|
|
266
266
|
def test_parse_video_info_prefers_story_default_play_url_by_data_size(self):
|
|
267
267
|
video_data = {
|
|
268
|
+
"duration": 9682,
|
|
268
269
|
"cover": {"url_list": ["https://cdn.example/thumb.jpg"]},
|
|
269
270
|
"bit_rate": [
|
|
270
271
|
{
|
|
271
272
|
"bit_rate": 0,
|
|
272
|
-
"duration": 9682,
|
|
273
273
|
"play_addr": {
|
|
274
274
|
"url_list": ["https://cdn.example/story-default.mp4"],
|
|
275
275
|
"width": 720,
|
|
@@ -279,7 +279,6 @@ class TestDouyinStorySupport(unittest.TestCase):
|
|
|
279
279
|
},
|
|
280
280
|
{
|
|
281
281
|
"bit_rate": 632,
|
|
282
|
-
"duration": 9682,
|
|
283
282
|
"play_addr": {
|
|
284
283
|
"url_list": ["https://cdn.example/720p.mp4"],
|
|
285
284
|
"width": 720,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|