feat: optimize screenshot rendering quality
- Use networkidle instead of load — waits for Discourse JS rendering - Wait for .cooked selector — smart content detection with fallback - Hide fixed/sticky elements via JS injection: - .d-header (top navigation bar) - .sidebar-wrapper (left sidebar) - .topic-navigation-wrapper, .footer-nav - Scroll to first post content for clean framing - Default to viewport screenshot (not full_page) — practical preview - Add screenshot_full_page config option for users who want full thread - Adjust viewport to 1280×900 (16:9-ish)
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
- 🔗 **自动检测** — 聊天中出现 `linux.do` 链接立即触发
|
||||
- 🛡️ **绕过 Cloudflare** — 使用 [Scrapling](https://github.com/D4Vinci/Scrapling) 的 StealthySession 自动解 Turnstile
|
||||
- 📸 **截图预览** — 全页面截图(1280×1024)
|
||||
- 📸 **智能截图** — 视口截图显示主帖,自动隐藏导航栏/侧边栏,可选全页模式
|
||||
- 📝 **内容摘要** — 提取标题 + 正文前 400 字
|
||||
- ⚡ **异步非阻塞** — Scrapling 在独立线程池运行,不阻塞 AstrBot 主循环
|
||||
- 💾 **缓存机制** — 30 分钟内相同链接直接返回缓存截图
|
||||
@@ -98,6 +98,7 @@ https://linux.do/t/topic/1378383
|
||||
| `cache_ttl` | 缓存有效期(秒),设为 0 关闭缓存 | 1800 |
|
||||
| `max_content_length` | 内容摘要最大长度(字符) | 400 |
|
||||
| `screenshot_timeout` | 截图超时(秒) | 15 |
|
||||
| `screenshot_full_page` | 全页截图模式(false=仅视口,更实用) | false |
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
|
||||
@@ -16,5 +16,11 @@
|
||||
"type": "int",
|
||||
"default": 15,
|
||||
"hint": "Max seconds to wait for page screenshot."
|
||||
},
|
||||
"screenshot_full_page": {
|
||||
"description": "Full page screenshot mode",
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"hint": "When false (default), captures viewport only — shows the first post cleanly. Set to true for full thread capture (larger file size)."
|
||||
}
|
||||
}
|
||||
|
||||
36
main.py
36
main.py
@@ -169,17 +169,43 @@ class LinuxDoPreviewPlugin(Star):
|
||||
return None
|
||||
|
||||
page = ctx.new_page()
|
||||
page.set_viewport_size({"width": 1280, "height": 1024})
|
||||
page.set_viewport_size({"width": 1280, "height": 900})
|
||||
|
||||
# 导航(已有 cf_clearance cookie,不应再触发 Cloudflare)
|
||||
page.goto(url, wait_until="load", timeout=timeout_ms)
|
||||
page.wait_for_timeout(3000)
|
||||
# ── 导航:等 networkidle 确保 JS 动态内容加载完成 ──
|
||||
page.goto(url, wait_until="networkidle", timeout=timeout_ms)
|
||||
|
||||
# ── 等待 Discourse 帖子内容渲染 ──
|
||||
try:
|
||||
page.wait_for_selector(".cooked", timeout=min(timeout_ms, 10000))
|
||||
except Exception:
|
||||
page.wait_for_timeout(3000) # 回退:固定等待
|
||||
|
||||
# ── 隐藏固定元素,避免截图杂乱/重复 ──
|
||||
page.evaluate("""() => {
|
||||
const hide = (sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) el.style.display = 'none';
|
||||
};
|
||||
hide('.d-header'); // 顶部导航栏
|
||||
hide('.sidebar-wrapper'); // 左侧边栏
|
||||
hide('.topic-navigation-wrapper'); // 帖子导航条
|
||||
hide('.footer-nav.visible'); // 底部导航
|
||||
|
||||
// 滚动到第一篇帖子正文
|
||||
const firstPost = document.querySelector('.cooked');
|
||||
if (firstPost) firstPost.scrollIntoView({block: 'start'});
|
||||
}""")
|
||||
|
||||
page.wait_for_timeout(1000) # 等待滚动稳定 + 懒加载图片
|
||||
|
||||
# ── 截图:默认视口模式,更实用的预览尺寸 ──
|
||||
full_page = self.config.get("screenshot_full_page", False)
|
||||
page.screenshot(
|
||||
path=str(save_path),
|
||||
full_page=True,
|
||||
full_page=full_page,
|
||||
timeout=timeout_ms,
|
||||
)
|
||||
|
||||
sz = save_path.stat().st_size
|
||||
logger.info(
|
||||
f"[LinuxDoPreview] 截图保存: {save_path.name} ({sz / 1024:.1f} KB)"
|
||||
|
||||
Reference in New Issue
Block a user