nonebot-plugin-htmlrender 0.6.2__tar.gz → 0.6.5__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_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/PKG-INFO +2 -2
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/browser.py +49 -15
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/config.py +25 -12
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/utils.py +13 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/pyproject.toml +2 -2
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/test_browser.py +24 -0
- nonebot_plugin_htmlrender-0.6.5/uv.lock +1782 -0
- nonebot_plugin_htmlrender-0.6.2/nonebot_plugin_htmlrender/compat.py +0 -24
- nonebot_plugin_htmlrender-0.6.2/uv.lock +0 -1729
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/.github/workflows/codecov.yml +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/.github/workflows/publish.yml +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/.gitignore +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/.pre-commit-config.yaml +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/.python-version +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/LICENSE +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/README.md +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/docs/example.md +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/docs/html2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/docs/md2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/docs/template2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/docs/text2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/.env.dev +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/bot.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/html2pic.html +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/templates/markdown.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/templates/mystyle.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/templates/progress.html.jinja2 +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/example/plugins/render/utils.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/consts.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/data_source.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/install.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/process.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/signal.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/github-markdown-light.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/katex/katex.min.b64_fonts.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/katex/katex.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/katex/mathtex-script-type.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/katex/mhchem.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/markdown.html +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/pygments-default.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/text.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/nonebot_plugin_htmlrender/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/conftest.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/resources/test_template_filter.png +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/templates/markdown.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/templates/mystyle.css +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/templates/progress.html.jinja2 +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/test_deprecated_decorator.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/test_htmlrender.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/test_install.py +0 -0
- {nonebot_plugin_htmlrender-0.6.2 → nonebot_plugin_htmlrender-0.6.5}/tests/test_process.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nonebot-plugin-htmlrender
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: 通过浏览器渲染图片
|
|
5
5
|
Project-URL: Homepage, https://github.com/kexue-z/nonebot-plugin-htmlrender
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/kexue-z/nonebot-plugin-htmlrender/issues
|
|
@@ -31,7 +31,7 @@ Requires-Python: <4.0,>=3.9
|
|
|
31
31
|
Requires-Dist: aiofiles>=0.8.0
|
|
32
32
|
Requires-Dist: jinja2>=3.0.3
|
|
33
33
|
Requires-Dist: markdown>=3.3.6
|
|
34
|
-
Requires-Dist: nonebot2>=2.2
|
|
34
|
+
Requires-Dist: nonebot2>=2.4.2
|
|
35
35
|
Requires-Dist: playwright>=1.48.0
|
|
36
36
|
Requires-Dist: pygments>=2.10.0
|
|
37
37
|
Requires-Dist: pymdown-extensions>=9.1
|
|
@@ -13,7 +13,7 @@ from playwright.async_api import (
|
|
|
13
13
|
|
|
14
14
|
from nonebot_plugin_htmlrender.config import plugin_config
|
|
15
15
|
from nonebot_plugin_htmlrender.install import install_browser
|
|
16
|
-
from nonebot_plugin_htmlrender.utils import proxy_settings, suppress_and_log
|
|
16
|
+
from nonebot_plugin_htmlrender.utils import proxy_settings, suppress_and_log, with_lock
|
|
17
17
|
|
|
18
18
|
_browser: Optional[Browser] = None
|
|
19
19
|
_playwright: Optional[Playwright] = None
|
|
@@ -53,8 +53,6 @@ async def init_browser(**kwargs) -> Browser:
|
|
|
53
53
|
Raises:
|
|
54
54
|
RuntimeError: 如果浏览器无法启动或安装失败。
|
|
55
55
|
"""
|
|
56
|
-
await install_browser() # update playwright when start
|
|
57
|
-
await check_playwright_env()
|
|
58
56
|
return await start_browser(**kwargs)
|
|
59
57
|
|
|
60
58
|
|
|
@@ -72,12 +70,11 @@ async def get_new_page(device_scale_factor: float = 2, **kwargs) -> AsyncIterato
|
|
|
72
70
|
"""
|
|
73
71
|
ctx = await get_browser()
|
|
74
72
|
page = await ctx.new_page(device_scale_factor=device_scale_factor, **kwargs)
|
|
75
|
-
|
|
73
|
+
async with page:
|
|
76
74
|
yield page
|
|
77
|
-
finally:
|
|
78
|
-
await page.close()
|
|
79
75
|
|
|
80
76
|
|
|
77
|
+
@with_lock
|
|
81
78
|
async def get_browser(**kwargs) -> Browser:
|
|
82
79
|
"""
|
|
83
80
|
获取浏览器实例。
|
|
@@ -117,6 +114,32 @@ async def _connect_via_cdp(**kwargs) -> Browser:
|
|
|
117
114
|
raise RuntimeError("Playwright 未初始化")
|
|
118
115
|
|
|
119
116
|
|
|
117
|
+
async def _connect(browser_type: str, **kwargs) -> Browser:
|
|
118
|
+
"""
|
|
119
|
+
通过 Playwright 协议连接浏览器。
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
browser_type (str): 浏览器类型。
|
|
123
|
+
**kwargs: 传递给`playwright.connect`的关键字参数。
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Browser: 启动的浏览器实例。
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
RuntimeError: 如果 Playwright 未初始化。
|
|
130
|
+
"""
|
|
131
|
+
_browser_cls: BrowserType = getattr(_playwright, browser_type)
|
|
132
|
+
kwargs["ws_endpoint"] = plugin_config.htmlrender_connect
|
|
133
|
+
logger.info(
|
|
134
|
+
f"正在使用 Playwright 协议连接 {browser_type}({plugin_config.htmlrender_connect})"
|
|
135
|
+
)
|
|
136
|
+
if _playwright is not None:
|
|
137
|
+
return await _browser_cls.connect(**kwargs)
|
|
138
|
+
else:
|
|
139
|
+
raise RuntimeError("Playwright 未初始化")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@with_lock
|
|
120
143
|
async def start_browser(**kwargs) -> Browser:
|
|
121
144
|
"""
|
|
122
145
|
启动 Playwright 浏览器实例。
|
|
@@ -128,24 +151,35 @@ async def start_browser(**kwargs) -> Browser:
|
|
|
128
151
|
Browser: 启动的浏览器实例。
|
|
129
152
|
"""
|
|
130
153
|
global _browser, _playwright
|
|
154
|
+
|
|
155
|
+
await shutdown_browser()
|
|
131
156
|
_playwright = await async_playwright().start()
|
|
132
157
|
|
|
133
158
|
if (
|
|
134
159
|
plugin_config.htmlrender_browser == "chromium"
|
|
135
160
|
and plugin_config.htmlrender_connect_over_cdp
|
|
136
161
|
):
|
|
137
|
-
|
|
162
|
+
_browser = await _connect_via_cdp(**kwargs)
|
|
163
|
+
elif plugin_config.htmlrender_connect:
|
|
164
|
+
_browser = await _connect(plugin_config.htmlrender_browser, **kwargs)
|
|
165
|
+
else:
|
|
166
|
+
if plugin_config.htmlrender_browser_channel:
|
|
167
|
+
kwargs["channel"] = plugin_config.htmlrender_browser_channel
|
|
138
168
|
|
|
139
|
-
|
|
140
|
-
|
|
169
|
+
if plugin_config.htmlrender_proxy_host:
|
|
170
|
+
kwargs["proxy"] = proxy_settings(plugin_config.htmlrender_proxy_host)
|
|
141
171
|
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
if plugin_config.htmlrender_browser_executable_path:
|
|
173
|
+
kwargs["executable_path"] = plugin_config.htmlrender_browser_executable_path
|
|
174
|
+
else:
|
|
175
|
+
try:
|
|
176
|
+
await check_playwright_env()
|
|
177
|
+
except RuntimeError:
|
|
178
|
+
await install_browser()
|
|
179
|
+
await check_playwright_env()
|
|
144
180
|
|
|
145
|
-
|
|
146
|
-
kwargs["executable_path"] = plugin_config.htmlrender_browser_executable_path
|
|
181
|
+
_browser = await _launch(plugin_config.htmlrender_browser, **kwargs)
|
|
147
182
|
|
|
148
|
-
_browser = await _launch(plugin_config.htmlrender_browser, **kwargs)
|
|
149
183
|
return _browser
|
|
150
184
|
|
|
151
185
|
|
|
@@ -164,7 +198,7 @@ async def check_playwright_env():
|
|
|
164
198
|
logger.info("Checking Playwright environment...")
|
|
165
199
|
try:
|
|
166
200
|
async with async_playwright() as p:
|
|
167
|
-
await p.
|
|
201
|
+
await getattr(p, plugin_config.htmlrender_browser).launch()
|
|
168
202
|
except Exception as e:
|
|
169
203
|
raise RuntimeError(
|
|
170
204
|
"Playwright environment is not set up correctly. "
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
from typing_extensions import Self
|
|
1
|
+
from typing import Any, Optional
|
|
3
2
|
|
|
4
3
|
from nonebot import get_driver, get_plugin_config
|
|
4
|
+
from nonebot.compat import model_validator
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
|
-
from nonebot_plugin_htmlrender.compat import model_validator
|
|
8
7
|
from nonebot_plugin_htmlrender.consts import BROWSER_CHANNEL_TYPES, BROWSER_ENGINE_TYPES
|
|
9
8
|
|
|
10
9
|
|
|
@@ -36,25 +35,39 @@ class Config(BaseModel):
|
|
|
36
35
|
htmlrender_connect_over_cdp: Optional[str] = Field(
|
|
37
36
|
default=None, description="通过 CDP 连接Playwright浏览器的端点地址。"
|
|
38
37
|
)
|
|
38
|
+
htmlrender_connect: Optional[str] = Field(
|
|
39
|
+
default=None, description="通过Playwright协议连接Playwright浏览器的端点地址。"
|
|
40
|
+
)
|
|
39
41
|
|
|
40
42
|
@model_validator(mode="after")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
@classmethod
|
|
44
|
+
def check_browser_channel(cls, data: Any) -> Any:
|
|
45
|
+
browser_channel = (
|
|
46
|
+
data.get("htmlrender_browser_channel")
|
|
47
|
+
if isinstance(data, dict)
|
|
48
|
+
else getattr(data, "htmlrender_browser_channel", None)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if browser_channel is not None and browser_channel not in BROWSER_CHANNEL_TYPES:
|
|
46
52
|
raise ValueError(
|
|
47
53
|
f"Invalid browser channel type. Must be one of {BROWSER_CHANNEL_TYPES}"
|
|
48
54
|
)
|
|
49
|
-
return
|
|
55
|
+
return data
|
|
50
56
|
|
|
51
57
|
@model_validator(mode="after")
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
@classmethod
|
|
59
|
+
def check_browser(cls, data: Any) -> Any:
|
|
60
|
+
browser = (
|
|
61
|
+
data.get("htmlrender_browser", "chromium")
|
|
62
|
+
if isinstance(data, dict)
|
|
63
|
+
else getattr(data, "htmlrender_browser", "chromium")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
if browser not in BROWSER_ENGINE_TYPES:
|
|
54
67
|
raise ValueError(
|
|
55
68
|
f"Invalid browser type. Must be one of {BROWSER_ENGINE_TYPES}"
|
|
56
69
|
)
|
|
57
|
-
return
|
|
70
|
+
return data
|
|
58
71
|
|
|
59
72
|
|
|
60
73
|
global_config = get_driver().config
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from asyncio import Lock
|
|
2
|
+
from collections.abc import Awaitable
|
|
1
3
|
from contextlib import contextmanager
|
|
2
4
|
from functools import wraps
|
|
3
5
|
import re
|
|
@@ -146,3 +148,14 @@ def proxy_settings(proxy_host: Optional[str]) -> Optional[dict]:
|
|
|
146
148
|
proxy["bypass"] = plugin_config.htmlrender_proxy_host_bypass
|
|
147
149
|
|
|
148
150
|
return proxy
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def with_lock(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]:
|
|
154
|
+
lock = Lock()
|
|
155
|
+
|
|
156
|
+
@wraps(func)
|
|
157
|
+
async def wrapper(*args, **kwargs) -> R:
|
|
158
|
+
async with lock:
|
|
159
|
+
return await func(*args, **kwargs)
|
|
160
|
+
|
|
161
|
+
return wrapper
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nonebot-plugin-htmlrender"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.5"
|
|
4
4
|
description = "通过浏览器渲染图片"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "kexue", email = "x@kexue-cloud.cn" }]
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
"aiofiles>=0.8.0",
|
|
11
11
|
"jinja2>=3.0.3",
|
|
12
12
|
"markdown>=3.3.6",
|
|
13
|
-
"nonebot2>=2.2
|
|
13
|
+
"nonebot2>=2.4.2",
|
|
14
14
|
"playwright>=1.48.0",
|
|
15
15
|
"pygments>=2.10.0",
|
|
16
16
|
"pymdown-extensions>=9.1",
|
|
@@ -39,6 +39,7 @@ def browser_config() -> dict[str, str]:
|
|
|
39
39
|
return {
|
|
40
40
|
"browser": "chromium",
|
|
41
41
|
"cdp": "ws://localhost:9222",
|
|
42
|
+
"pwp": "ws://localhost:3000/chromium/playwright",
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
|
|
@@ -169,6 +170,29 @@ async def test_connect_via_cdp(
|
|
|
169
170
|
assert browser == mock_browser
|
|
170
171
|
|
|
171
172
|
|
|
173
|
+
@pytest.mark.asyncio
|
|
174
|
+
async def test_connect(
|
|
175
|
+
mocker: MockerFixture, mock_browser: Browser, browser_config: dict[str, str]
|
|
176
|
+
) -> None:
|
|
177
|
+
"""测试通过Playwright协议连接浏览器"""
|
|
178
|
+
from nonebot_plugin_htmlrender.browser import start_browser
|
|
179
|
+
|
|
180
|
+
mocker.patch(
|
|
181
|
+
"nonebot_plugin_htmlrender.browser._connect", return_value=mock_browser
|
|
182
|
+
)
|
|
183
|
+
mocker.patch(
|
|
184
|
+
"nonebot_plugin_htmlrender.browser.plugin_config.htmlrender_browser",
|
|
185
|
+
browser_config["browser"],
|
|
186
|
+
)
|
|
187
|
+
mocker.patch(
|
|
188
|
+
"nonebot_plugin_htmlrender.browser.plugin_config.htmlrender_connect",
|
|
189
|
+
browser_config["pwp"],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
browser = await start_browser()
|
|
193
|
+
assert browser == mock_browser
|
|
194
|
+
|
|
195
|
+
|
|
172
196
|
@pytest.mark.parametrize(
|
|
173
197
|
("proxy_url", "expected"),
|
|
174
198
|
[
|