nonebot-plugin-parser 2.6.6__tar.gz → 2.6.8__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.6.6 → nonebot_plugin_parser-2.6.8}/PKG-INFO +5 -1
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/README.md +4 -0
- nonebot_plugin_parser-2.6.8/pyproject.toml +224 -0
- nonebot_plugin_parser-2.6.6/pyproject.toml → nonebot_plugin_parser-2.6.8/pyproject.toml.orig +2 -2
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/config.py +7 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/download/__init__.py +15 -37
- nonebot_plugin_parser-2.6.8/src/nonebot_plugin_parser/download/rich.py +25 -0
- nonebot_plugin_parser-2.6.8/src/nonebot_plugin_parser/matchers/filter.py +82 -0
- nonebot_plugin_parser-2.6.8/src/nonebot_plugin_parser/parsers/douyin/__init__.py +118 -0
- nonebot_plugin_parser-2.6.8/src/nonebot_plugin_parser/parsers/douyin/aweme.py +82 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/xiaohongshu/__init__.py +1 -1
- nonebot_plugin_parser-2.6.6/src/nonebot_plugin_parser/matchers/filter.py +0 -72
- nonebot_plugin_parser-2.6.6/src/nonebot_plugin_parser/parsers/douyin/__init__.py +0 -137
- nonebot_plugin_parser-2.6.6/src/nonebot_plugin_parser/parsers/douyin/slides.py +0 -63
- nonebot_plugin_parser-2.6.6/src/nonebot_plugin_parser/parsers/douyin/video.py +0 -103
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/constants.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/download/task.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/exception.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/helper.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/matchers/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/matchers/rule.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/acfun/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/acfun/video.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/base.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/data.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/kuaishou/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/kuaishou/states.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/nga.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/task.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/tiktok.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/twitter.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/utils.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/weibo/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/weibo/article.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/weibo/common.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/weibo/show.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/xiaohongshu/common.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/xiaohongshu/discovery.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/xiaohongshu/explore.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/youtube/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/parsers/youtube/meta.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/base.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/common/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/common/assets.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/common/font.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/common/renderer.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/default.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/htmlrender.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW.ttf +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/__init__.py +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/avatar.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/1.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/2.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/3.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/4.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/5.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/6.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/7.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/8.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/fallback_pic/9.jpg +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/play.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/renders/templates/card.html.jinja2 +0 -0
- {nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/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.6.
|
|
3
|
+
Version: 2.6.8
|
|
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
|
|
@@ -315,6 +315,10 @@ parser_emoji_cdn="https://emojicdn.elk.sh"
|
|
|
315
315
|
|
|
316
316
|
# [可选] emoji 渲染样式 "apple", "google", "twitter", "facebook"(默认)
|
|
317
317
|
parser_emoji_style="facebook"
|
|
318
|
+
|
|
319
|
+
# [可选] 是否启用群组黑名单模式(默认启用,即所有群聊的解析都是开启的)
|
|
320
|
+
parser_group_blacklist_enabled=True
|
|
321
|
+
|
|
318
322
|
```
|
|
319
323
|
|
|
320
324
|
</details>
|
|
@@ -262,6 +262,10 @@ parser_emoji_cdn="https://emojicdn.elk.sh"
|
|
|
262
262
|
|
|
263
263
|
# [可选] emoji 渲染样式 "apple", "google", "twitter", "facebook"(默认)
|
|
264
264
|
parser_emoji_style="facebook"
|
|
265
|
+
|
|
266
|
+
# [可选] 是否启用群组黑名单模式(默认启用,即所有群聊的解析都是开启的)
|
|
267
|
+
parser_group_blacklist_enabled=True
|
|
268
|
+
|
|
265
269
|
```
|
|
266
270
|
|
|
267
271
|
</details>
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "nonebot-plugin-parser"
|
|
3
|
+
version = "2.6.8"
|
|
4
|
+
description = "NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
keywords = [
|
|
8
|
+
"acfun",
|
|
9
|
+
"bilibili",
|
|
10
|
+
"douyin",
|
|
11
|
+
"kuaishou",
|
|
12
|
+
"nga",
|
|
13
|
+
"nonebot",
|
|
14
|
+
"nonebot2",
|
|
15
|
+
"tiktok",
|
|
16
|
+
"twitter",
|
|
17
|
+
"video",
|
|
18
|
+
"weibo",
|
|
19
|
+
"xiaohongshu",
|
|
20
|
+
"youtube",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 5 - Production/Stable",
|
|
24
|
+
"Framework :: AsyncIO",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Programming Language :: Python :: 3.10",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Programming Language :: Python :: 3.14",
|
|
33
|
+
"Topic :: Communications :: Chat",
|
|
34
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
35
|
+
"Topic :: Multimedia :: Video",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"rich>=13.0.0",
|
|
39
|
+
"pillow>=11.0.0",
|
|
40
|
+
"aiofiles>=25.1.0",
|
|
41
|
+
"httpx>=0.27.2,<1.0.0",
|
|
42
|
+
"msgspec>=0.20.0,<1.0.0",
|
|
43
|
+
"nonebot2>=2.5.0,<3.0.0",
|
|
44
|
+
"apilmoji[rich]>=0.3.1,<1.0.0",
|
|
45
|
+
"beautifulsoup4>=4.12.0,<5.0.0",
|
|
46
|
+
"curl_cffi>=0.13.0,<1.0.0,!=0.14.0",
|
|
47
|
+
"bilibili-api-python>=17.4.2,<18.0.0",
|
|
48
|
+
"nonebot-plugin-uninfo>=0.10.1,<1.0.0",
|
|
49
|
+
"nonebot-plugin-alconna>=0.60.4,<1.0.0",
|
|
50
|
+
"nonebot-plugin-localstore>=0.7.4,<1.0.0",
|
|
51
|
+
"nonebot-plugin-apscheduler>=0.5.0,<1.0.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[[project.authors]]
|
|
55
|
+
name = "fllesser"
|
|
56
|
+
email = "fllessive@gmail.com"
|
|
57
|
+
|
|
58
|
+
[project.urls]
|
|
59
|
+
IssueTracker = "https://github.com/fllesser/nonebot-plugin-parser/issues"
|
|
60
|
+
Release = "https://github.com/fllesser/nonebot-plugin-parser/releases"
|
|
61
|
+
Repository = "https://github.com/fllesser/nonebot-plugin-parser"
|
|
62
|
+
|
|
63
|
+
[project.optional-dependencies]
|
|
64
|
+
htmlkit = ["nonebot-plugin-htmlkit>=0.1.0rc4"]
|
|
65
|
+
htmlrender = ["nonebot-plugin-htmlrender>=0.6.7"]
|
|
66
|
+
ytdlp = ["yt-dlp[default]>=2025.2.21"]
|
|
67
|
+
emosvg = ["emosvg>=0.1.7"]
|
|
68
|
+
all = [
|
|
69
|
+
"nonebot-plugin-htmlkit>=0.1.0rc4",
|
|
70
|
+
"nonebot-plugin-htmlrender>=0.6.7",
|
|
71
|
+
"yt-dlp[default]>=2026.3.13",
|
|
72
|
+
"emosvg>=0.1.7",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[dependency-groups]
|
|
76
|
+
dev = [
|
|
77
|
+
"nonebot2[fastapi]>=2.5.0,<3.0.0",
|
|
78
|
+
"nonebot-adapter-onebot>=2.4.6",
|
|
79
|
+
"ruff>=0.15.6,<1.0.0",
|
|
80
|
+
{ include-group = "extras" },
|
|
81
|
+
{ include-group = "test" },
|
|
82
|
+
]
|
|
83
|
+
extras = [
|
|
84
|
+
"emosvg>=0.1.7",
|
|
85
|
+
"nonebot-plugin-htmlkit>=0.1.0rc4",
|
|
86
|
+
"nonebot-plugin-htmlrender>=0.6.7",
|
|
87
|
+
"yt-dlp[default]>=2026.3.13",
|
|
88
|
+
"types-yt-dlp>=2026.3.13.20260314",
|
|
89
|
+
]
|
|
90
|
+
test = [
|
|
91
|
+
"nonebug>=0.4.4,<1.0.0",
|
|
92
|
+
"poethepoet>=0.42.1,<1.0.0",
|
|
93
|
+
"pytest-asyncio>=1.3.0,<1.4.0",
|
|
94
|
+
"pytest-cov>=7.0.0",
|
|
95
|
+
"pytest-xdist>=3.8.0,<4.0.0",
|
|
96
|
+
"respx>=0.22.0",
|
|
97
|
+
]
|
|
98
|
+
pydantic-v1 = ["pydantic<2.0.0"]
|
|
99
|
+
pydantic-v2 = ["pydantic>=2.0.0"]
|
|
100
|
+
telegram = ["nonebot-adapter-telegram>=0.1.0b20"]
|
|
101
|
+
|
|
102
|
+
[build-system]
|
|
103
|
+
requires = ["uv_build>=0.11.0,<0.12.0"]
|
|
104
|
+
build-backend = "uv_build"
|
|
105
|
+
|
|
106
|
+
[tool.uv]
|
|
107
|
+
conflicts = [[
|
|
108
|
+
{ group = "pydantic-v1" },
|
|
109
|
+
{ group = "pydantic-v2" },
|
|
110
|
+
{ group = "telegram" },
|
|
111
|
+
]]
|
|
112
|
+
|
|
113
|
+
[tool.bumpversion]
|
|
114
|
+
tag = true
|
|
115
|
+
commit = true
|
|
116
|
+
current_version = "2.6.8"
|
|
117
|
+
message = "release: bump vesion from {current_version} to {new_version}"
|
|
118
|
+
|
|
119
|
+
[[tool.bumpversion.files]]
|
|
120
|
+
filename = "uv.lock"
|
|
121
|
+
search = """
|
|
122
|
+
name = "nonebot-plugin-parser"
|
|
123
|
+
version = "{current_version}""""
|
|
124
|
+
replace = """
|
|
125
|
+
name = "nonebot-plugin-parser"
|
|
126
|
+
version = "{new_version}""""
|
|
127
|
+
|
|
128
|
+
[tool.coverage.report]
|
|
129
|
+
show_missing = true
|
|
130
|
+
exclude_lines = [
|
|
131
|
+
"raise NotImplementedError",
|
|
132
|
+
"if TYPE_CHECKING:",
|
|
133
|
+
"@overload",
|
|
134
|
+
"except",
|
|
135
|
+
"raise",
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
[tool.nonebot]
|
|
139
|
+
plugins = ["nonebot_plugin_parser"]
|
|
140
|
+
|
|
141
|
+
[[tool.nonebot.adapters]]
|
|
142
|
+
name = "Onebot V11"
|
|
143
|
+
module_name = "nonebot.adapters.onebot.v11"
|
|
144
|
+
|
|
145
|
+
[tool.poe.tasks]
|
|
146
|
+
test-other = "pytest tests/others --cov=src --cov-report=xml --junitxml=junit.xml -n auto"
|
|
147
|
+
test-parser = "pytest tests/parsers --cov=src --cov-report=xml --junitxml=junit.xml -n auto"
|
|
148
|
+
test-render = "pytest tests/renders --cov=src --cov-report=xml --junitxml=junit.xml"
|
|
149
|
+
bump = "bump-my-version bump"
|
|
150
|
+
show-bump = "bump-my-version show-bump"
|
|
151
|
+
|
|
152
|
+
[tool.basedpyright]
|
|
153
|
+
pythonVersion = "3.10"
|
|
154
|
+
pythonPlatform = "All"
|
|
155
|
+
typeCheckingMode = "standard"
|
|
156
|
+
disableBytesTypePromotions = true
|
|
157
|
+
|
|
158
|
+
[tool.basedpyright.defineConstant]
|
|
159
|
+
PYDANTIC_V2 = true
|
|
160
|
+
|
|
161
|
+
[[tool.basedpyright.executionEnvironments]]
|
|
162
|
+
root = "./tests"
|
|
163
|
+
extraPaths = ["./src"]
|
|
164
|
+
|
|
165
|
+
[[tool.basedpyright.executionEnvironments]]
|
|
166
|
+
root = "./src"
|
|
167
|
+
|
|
168
|
+
[tool.pytest]
|
|
169
|
+
addopts = [
|
|
170
|
+
"--import-mode=prepend",
|
|
171
|
+
"--strict-markers",
|
|
172
|
+
"--tb=short",
|
|
173
|
+
"-ra",
|
|
174
|
+
"-s",
|
|
175
|
+
"-v",
|
|
176
|
+
]
|
|
177
|
+
pythonpath = ["src"]
|
|
178
|
+
asyncio_mode = "auto"
|
|
179
|
+
asyncio_default_fixture_loop_scope = "session"
|
|
180
|
+
|
|
181
|
+
[tool.ruff]
|
|
182
|
+
line-length = 120
|
|
183
|
+
|
|
184
|
+
[tool.ruff.format]
|
|
185
|
+
line-ending = "lf"
|
|
186
|
+
|
|
187
|
+
[tool.ruff.lint]
|
|
188
|
+
select = [
|
|
189
|
+
"F",
|
|
190
|
+
"W",
|
|
191
|
+
"E",
|
|
192
|
+
"I",
|
|
193
|
+
"UP",
|
|
194
|
+
"Q",
|
|
195
|
+
"PYI",
|
|
196
|
+
"T10",
|
|
197
|
+
"T20",
|
|
198
|
+
"C4",
|
|
199
|
+
"PT",
|
|
200
|
+
"TID",
|
|
201
|
+
"ASYNC",
|
|
202
|
+
"RUF",
|
|
203
|
+
]
|
|
204
|
+
ignore = [
|
|
205
|
+
"E402",
|
|
206
|
+
"UP037",
|
|
207
|
+
"RUF001",
|
|
208
|
+
"RUF002",
|
|
209
|
+
"RUF003",
|
|
210
|
+
"W191",
|
|
211
|
+
"TID252",
|
|
212
|
+
"ASYNC240",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
[tool.ruff.lint.isort]
|
|
216
|
+
length-sort = true
|
|
217
|
+
known-first-party = [
|
|
218
|
+
"nonebot_plugin_parser",
|
|
219
|
+
"tests/*",
|
|
220
|
+
]
|
|
221
|
+
extra-standard-library = ["typing_extensions"]
|
|
222
|
+
|
|
223
|
+
[tool.ruff.lint.pyupgrade]
|
|
224
|
+
keep-runtime-typing = true
|
nonebot_plugin_parser-2.6.6/pyproject.toml → nonebot_plugin_parser-2.6.8/pyproject.toml.orig
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nonebot-plugin-parser"
|
|
3
|
-
version = "2.6.
|
|
3
|
+
version = "2.6.8"
|
|
4
4
|
description = "NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -115,7 +115,7 @@ conflicts = [
|
|
|
115
115
|
[tool.bumpversion]
|
|
116
116
|
tag = true
|
|
117
117
|
commit = true
|
|
118
|
-
current_version = "2.6.
|
|
118
|
+
current_version = "2.6.8"
|
|
119
119
|
message = "release: bump vesion from {current_version} to {new_version}"
|
|
120
120
|
|
|
121
121
|
[[tool.bumpversion.files]]
|
{nonebot_plugin_parser-2.6.6 → nonebot_plugin_parser-2.6.8}/src/nonebot_plugin_parser/config.py
RENAMED
|
@@ -56,6 +56,8 @@ class Config(BaseModel):
|
|
|
56
56
|
"""Pilmoji 表情 CDN"""
|
|
57
57
|
parser_emoji_style: EmojiStyle = EmojiStyle.FACEBOOK
|
|
58
58
|
"""Pilmoji 表情样式"""
|
|
59
|
+
parser_group_blacklist_enabled: bool = True
|
|
60
|
+
"""是否启用群组黑名单模式(默认启用,即所有群聊的解析都是开启的)"""
|
|
59
61
|
|
|
60
62
|
@property
|
|
61
63
|
def nickname(self) -> str:
|
|
@@ -182,6 +184,11 @@ class Config(BaseModel):
|
|
|
182
184
|
"""Pilmoji 表情样式"""
|
|
183
185
|
return self.parser_emoji_style
|
|
184
186
|
|
|
187
|
+
@property
|
|
188
|
+
def group_blacklist_enabled(self) -> bool:
|
|
189
|
+
"""是否启用群组黑名单模式"""
|
|
190
|
+
return self.parser_group_blacklist_enabled
|
|
191
|
+
|
|
185
192
|
|
|
186
193
|
pconfig: Config = get_plugin_config(Config)
|
|
187
194
|
"""插件配置"""
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from functools import partial
|
|
4
|
-
from contextlib import contextmanager
|
|
5
3
|
from urllib.parse import urljoin
|
|
6
4
|
|
|
7
5
|
import httpx
|
|
8
6
|
import aiofiles
|
|
9
7
|
import curl_cffi
|
|
10
8
|
from nonebot import logger, get_driver
|
|
11
|
-
from rich.progress import (
|
|
12
|
-
Progress,
|
|
13
|
-
BarColumn,
|
|
14
|
-
TextColumn,
|
|
15
|
-
DownloadColumn,
|
|
16
|
-
)
|
|
17
9
|
|
|
10
|
+
from .rich import progress_bar, add_progress_task
|
|
18
11
|
from .task import auto_task
|
|
19
12
|
from ..utils import merge_av, safe_unlink, generate_file_name, is_module_available
|
|
20
13
|
from ..config import pconfig
|
|
@@ -31,22 +24,6 @@ class StreamDownloader:
|
|
|
31
24
|
async def aclose(self):
|
|
32
25
|
await self.client.aclose()
|
|
33
26
|
|
|
34
|
-
@staticmethod
|
|
35
|
-
@contextmanager
|
|
36
|
-
def rich_progress(
|
|
37
|
-
desc: str,
|
|
38
|
-
total: int | None = None,
|
|
39
|
-
):
|
|
40
|
-
with Progress(
|
|
41
|
-
TextColumn("[bold blue]{task.description}", justify="right"),
|
|
42
|
-
BarColumn(bar_width=None),
|
|
43
|
-
"[progress.percentage]{task.percentage:>3.1f}%",
|
|
44
|
-
"•",
|
|
45
|
-
DownloadColumn(),
|
|
46
|
-
) as progress:
|
|
47
|
-
task_id = progress.add_task(description=desc, total=total)
|
|
48
|
-
yield partial(progress.update, task_id)
|
|
49
|
-
|
|
50
27
|
@staticmethod
|
|
51
28
|
def _validate_content_length(
|
|
52
29
|
response: httpx.Response | curl_cffi.Response,
|
|
@@ -84,10 +61,11 @@ class StreamDownloader:
|
|
|
84
61
|
response.raise_for_status()
|
|
85
62
|
content_length = self._validate_content_length(response)
|
|
86
63
|
|
|
87
|
-
with
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
64
|
+
with progress_bar:
|
|
65
|
+
update_progress = add_progress_task(
|
|
66
|
+
f"httpx | {file_path.name}",
|
|
67
|
+
content_length,
|
|
68
|
+
)
|
|
91
69
|
async with aiofiles.open(file_path, "wb") as file:
|
|
92
70
|
async for chunk in response.aiter_bytes(chunk_size):
|
|
93
71
|
await file.write(chunk)
|
|
@@ -112,10 +90,11 @@ class StreamDownloader:
|
|
|
112
90
|
response.raise_for_status()
|
|
113
91
|
content_length = self._validate_content_length(response)
|
|
114
92
|
|
|
115
|
-
with
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
93
|
+
with progress_bar:
|
|
94
|
+
update_progress = add_progress_task(
|
|
95
|
+
f"curl_cffi | {file_path.name}",
|
|
96
|
+
content_length,
|
|
97
|
+
)
|
|
119
98
|
async with aiofiles.open(file_path, "wb") as file:
|
|
120
99
|
async for chunk in response.aiter_content(chunk_size=8192):
|
|
121
100
|
await file.write(chunk)
|
|
@@ -241,15 +220,14 @@ class StreamDownloader:
|
|
|
241
220
|
video_path = pconfig.cache_dir / video_name
|
|
242
221
|
|
|
243
222
|
try:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
223
|
+
with progress_bar:
|
|
224
|
+
async with aiofiles.open(video_path, "wb") as f:
|
|
225
|
+
update_progress = add_progress_task(desc=video_name)
|
|
247
226
|
for url in await self._get_m3u8_slices(m3u8_url):
|
|
248
227
|
async with self.client.stream("GET", url, headers=ext_headers) as response:
|
|
249
228
|
async for chunk in response.aiter_bytes(chunk_size=1024 * 1024):
|
|
250
229
|
await f.write(chunk)
|
|
251
|
-
|
|
252
|
-
update_progress(advance=len(chunk), total=total_size)
|
|
230
|
+
update_progress(advance=len(chunk))
|
|
253
231
|
except httpx.HTTPError:
|
|
254
232
|
await safe_unlink(video_path)
|
|
255
233
|
logger.exception("m3u8 视频下载失败")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
|
|
3
|
+
from rich.progress import (
|
|
4
|
+
Progress,
|
|
5
|
+
BarColumn,
|
|
6
|
+
TextColumn,
|
|
7
|
+
DownloadColumn,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
progress_bar: Progress = Progress(
|
|
11
|
+
TextColumn("[bold blue]{task.description}", justify="right"),
|
|
12
|
+
BarColumn(bar_width=None),
|
|
13
|
+
"[progress.percentage]{task.percentage:>3.1f}%",
|
|
14
|
+
"•",
|
|
15
|
+
DownloadColumn(),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_progress_task(
|
|
20
|
+
desc: str,
|
|
21
|
+
total: int | None = None,
|
|
22
|
+
):
|
|
23
|
+
task_id = progress_bar.add_task(description=desc, total=total)
|
|
24
|
+
progress_bar.start_task(task_id)
|
|
25
|
+
return partial(progress_bar.update, task_id)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from nonebot import on_command
|
|
5
|
+
from nonebot.rule import to_me
|
|
6
|
+
from nonebot.matcher import Matcher
|
|
7
|
+
from nonebot.permission import SUPERUSER
|
|
8
|
+
from nonebot_plugin_uninfo import ADMIN, Session, UniSession
|
|
9
|
+
|
|
10
|
+
from ..config import pconfig
|
|
11
|
+
|
|
12
|
+
_DISABLED_GROUPS_PATH: Path = pconfig.data_dir / "disabled_groups.json"
|
|
13
|
+
_GROUP_SET_PATH: Path = pconfig.data_dir / "group_set.json"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _load_or_initialize_set() -> set[str]:
|
|
17
|
+
"""加载群配置"""
|
|
18
|
+
# 判断是否存在
|
|
19
|
+
if not _GROUP_SET_PATH.exists():
|
|
20
|
+
if _DISABLED_GROUPS_PATH.exists():
|
|
21
|
+
# 迁移旧的关闭解析名单
|
|
22
|
+
_DISABLED_GROUPS_PATH.rename(_GROUP_SET_PATH)
|
|
23
|
+
else:
|
|
24
|
+
_GROUP_SET_PATH.write_text(json.dumps([]))
|
|
25
|
+
|
|
26
|
+
return set(json.loads(_GROUP_SET_PATH.read_text()))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_GROUP_SET: set[str] = _load_or_initialize_set()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _save_group_set():
|
|
33
|
+
_GROUP_SET_PATH.write_text(json.dumps(list(_GROUP_SET)))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _add_group(group_key: str):
|
|
37
|
+
_GROUP_SET.add(group_key)
|
|
38
|
+
_save_group_set()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _remove_group(group_key: str):
|
|
42
|
+
_GROUP_SET.discard(group_key)
|
|
43
|
+
_save_group_set()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_group_key(session: Session) -> str:
|
|
47
|
+
"""获取群组的唯一标识符 由平台名称和会话场景 ID 组成,例如 `QQClient_123456789`"""
|
|
48
|
+
return f"{session.scope}_{session.scene_path}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_enabled(session: Session = UniSession()) -> bool:
|
|
52
|
+
"""判断当前会话是否在关闭解析的名单中"""
|
|
53
|
+
if session.scene.is_private:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
group_key = _get_group_key(session)
|
|
57
|
+
if pconfig.group_blacklist_enabled:
|
|
58
|
+
return group_key not in _GROUP_SET
|
|
59
|
+
else:
|
|
60
|
+
return group_key in _GROUP_SET
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@on_command("开启解析", rule=to_me(), permission=SUPERUSER | ADMIN(), block=True).handle()
|
|
64
|
+
async def _(matcher: Matcher, session: Session = UniSession()):
|
|
65
|
+
"""开启解析"""
|
|
66
|
+
group_key = _get_group_key(session)
|
|
67
|
+
if pconfig.group_blacklist_enabled:
|
|
68
|
+
_remove_group(group_key)
|
|
69
|
+
else:
|
|
70
|
+
_add_group(group_key)
|
|
71
|
+
await matcher.finish("解析已开启")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@on_command("关闭解析", rule=to_me(), permission=SUPERUSER | ADMIN(), block=True).handle()
|
|
75
|
+
async def _(matcher: Matcher, session: Session = UniSession()):
|
|
76
|
+
"""关闭解析"""
|
|
77
|
+
group_key = _get_group_key(session)
|
|
78
|
+
if pconfig.group_blacklist_enabled:
|
|
79
|
+
_add_group(group_key)
|
|
80
|
+
else:
|
|
81
|
+
_remove_group(group_key)
|
|
82
|
+
await matcher.finish("解析已关闭")
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from httpx import AsyncClient
|
|
6
|
+
|
|
7
|
+
from ..base import (
|
|
8
|
+
COMMON_TIMEOUT,
|
|
9
|
+
Platform,
|
|
10
|
+
BaseParser,
|
|
11
|
+
PlatformEnum,
|
|
12
|
+
ParseException,
|
|
13
|
+
handle,
|
|
14
|
+
)
|
|
15
|
+
from .aweme import decoder
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DouyinParser(BaseParser):
|
|
19
|
+
platform: ClassVar[Platform] = Platform(name=PlatformEnum.DOUYIN, display_name="抖音")
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.headers.update(
|
|
24
|
+
{
|
|
25
|
+
"Origin": "https://open.douyin.com",
|
|
26
|
+
"Referer": "https://open.douyin.com/",
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# https://v.douyin.com/_2ljF4AmKL8
|
|
31
|
+
@handle("v.douyin", r"v\.douyin\.com/[a-zA-Z0-9_\-]+")
|
|
32
|
+
@handle("jx.douyin", r"jx\.douyin\.com/[a-zA-Z0-9_\-]+")
|
|
33
|
+
async def _parse_short_link(self, searched: re.Match[str]):
|
|
34
|
+
url = f"https://{searched.group(0)}"
|
|
35
|
+
return await self.parse_with_redirect(url)
|
|
36
|
+
|
|
37
|
+
# https://www.douyin.com/video/7521023890996514083
|
|
38
|
+
# https://www.douyin.com/note/7469411074119322899
|
|
39
|
+
@handle("douyin", r"douyin\.com/[a-z]+/(?P<aweme_id>\d+)")
|
|
40
|
+
@handle("iesdouyin", r"iesdouyin\.com/share/[a-z]+/(?P<aweme_id>\d+)")
|
|
41
|
+
@handle("m.douyin", r"m\.douyin\.com/share/[a-z]+/(?P<aweme_id>\d+)")
|
|
42
|
+
# https://jingxuan.douyin.com/m/video/7574300896016862490?app=yumme&utm_source=copy_link
|
|
43
|
+
@handle("jingxuan.douyin", r"jingxuan\.douyin.com/m/[a-z]+/(?P<aweme_id>\d+)")
|
|
44
|
+
async def _parse_douyin(self, searched: re.Match[str]):
|
|
45
|
+
aweme_id = searched.group("aweme_id")
|
|
46
|
+
return await self.parse_aweme(aweme_id)
|
|
47
|
+
|
|
48
|
+
async def parse_aweme(self, aweme_id: str):
|
|
49
|
+
async with AsyncClient(
|
|
50
|
+
headers=self.headers,
|
|
51
|
+
timeout=COMMON_TIMEOUT,
|
|
52
|
+
follow_redirects=True,
|
|
53
|
+
verify=False,
|
|
54
|
+
) as client:
|
|
55
|
+
response = await client.get(
|
|
56
|
+
"https://www.douyin.com/aweme/v1/web/aweme/detail/",
|
|
57
|
+
params={"aweme_id": aweme_id, "aid": "6383"},
|
|
58
|
+
)
|
|
59
|
+
if response.status_code != 200:
|
|
60
|
+
raise ParseException(f"status: {response.status_code}")
|
|
61
|
+
aweme = decoder.decode(response.content).aweme_detail
|
|
62
|
+
|
|
63
|
+
# 作者
|
|
64
|
+
author = self.create_author(
|
|
65
|
+
aweme.author.nickname,
|
|
66
|
+
aweme.author.avatar_thumb.url_list[-1],
|
|
67
|
+
aweme.author.signature,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 先以部分数据构建结果,后续再填充内容,避免使用临时变量
|
|
71
|
+
result = self.result(
|
|
72
|
+
text=aweme.share_info.text,
|
|
73
|
+
author=author,
|
|
74
|
+
timestamp=aweme.create_time,
|
|
75
|
+
url=aweme.share_url.split("?")[0],
|
|
76
|
+
)
|
|
77
|
+
if music := aweme.music:
|
|
78
|
+
if not music.is_original_sound:
|
|
79
|
+
if music.play_url.uri == "":
|
|
80
|
+
extra = json.loads(music.extra)
|
|
81
|
+
music_url = extra.get("original_song_url")
|
|
82
|
+
else:
|
|
83
|
+
music_url = music.play_url.uri
|
|
84
|
+
if music_url:
|
|
85
|
+
result.contents.append(
|
|
86
|
+
self.create_audio(
|
|
87
|
+
url_or_task=music_url,
|
|
88
|
+
duration=music.duration,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# 添加图片内容
|
|
93
|
+
if images := aweme.images:
|
|
94
|
+
for image in images:
|
|
95
|
+
if image.clip_type == 2 or image.clip_type is None:
|
|
96
|
+
result.contents.append(
|
|
97
|
+
self.create_image(
|
|
98
|
+
url_or_task=image.url_list[-1],
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
elif image_video := image.video:
|
|
102
|
+
result.contents.extend(
|
|
103
|
+
[
|
|
104
|
+
self.create_image(
|
|
105
|
+
url_or_task=image_video.cover.url_list[-1],
|
|
106
|
+
),
|
|
107
|
+
self.create_video(url_or_task=image_video.play_addr.url),
|
|
108
|
+
]
|
|
109
|
+
)
|
|
110
|
+
# 添加视频内容
|
|
111
|
+
elif video := aweme.video:
|
|
112
|
+
result.video = self.create_video(
|
|
113
|
+
video.play_addr.url,
|
|
114
|
+
video.cover_original_scale.url_list[-1] if video.cover_original_scale else video.cover.url_list[-1],
|
|
115
|
+
video.duration // 1000,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return result
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from msgspec import Struct, field
|
|
2
|
+
from msgspec.json import Decoder
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Addr(Struct):
|
|
6
|
+
uri: str
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def url(self) -> str:
|
|
10
|
+
return f"https://aweme.snssdk.com/aweme/v1/play/?video_id={self.uri}&ratio=1080p&line=0"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UrlList(Struct):
|
|
14
|
+
url_list: list[str]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Author(Struct):
|
|
18
|
+
uid: str
|
|
19
|
+
nickname: str
|
|
20
|
+
avatar_thumb: UrlList
|
|
21
|
+
"""头像"""
|
|
22
|
+
signature: str | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Video(Struct):
|
|
26
|
+
cover: UrlList
|
|
27
|
+
duration: int
|
|
28
|
+
"""视频时长(/1000)"""
|
|
29
|
+
play_addr: Addr
|
|
30
|
+
cover_original_scale: UrlList | None = None
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def url(self) -> str:
|
|
34
|
+
return self.play_addr.url
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Image(Struct):
|
|
38
|
+
url_list: list[str]
|
|
39
|
+
uri: str = field(default="")
|
|
40
|
+
clip_type: int | None = field(default=None)
|
|
41
|
+
"""=2 or None 是普通图片"""
|
|
42
|
+
video: Video | None = field(default=None)
|
|
43
|
+
"""Live Photo 视频"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MusicPlayUrl(Struct):
|
|
47
|
+
uri: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Music(Struct):
|
|
51
|
+
duration: int
|
|
52
|
+
mid: str
|
|
53
|
+
play_url: MusicPlayUrl
|
|
54
|
+
extra: str
|
|
55
|
+
is_original_sound: bool
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ShareInfo(Struct):
|
|
59
|
+
share_desc: str
|
|
60
|
+
share_desc_info: str
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def text(self) -> str:
|
|
64
|
+
return self.share_desc_info.replace(f"#{self.share_desc}#", "", 1)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Aweme(Struct):
|
|
68
|
+
aweme_id: str
|
|
69
|
+
author: Author
|
|
70
|
+
share_info: ShareInfo
|
|
71
|
+
create_time: int
|
|
72
|
+
share_url: str
|
|
73
|
+
images: list[Image] | None = field(default=None)
|
|
74
|
+
music: Music | None = field(default=None)
|
|
75
|
+
video: Video | None = field(default=None)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Response(Struct):
|
|
79
|
+
aweme_detail: Aweme
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
decoder = Decoder(Response)
|