play.wenpai.net/scripts/play-capture
elementary-qa 472f131e4e feat: 标准化产品截图第二批 — WPMind 2 张 + manifest 补齐 5 插件
- 新增 wpmind 主面板 + 插件列表截图 (PNG+WebP)
- manifest.json 补齐 wpslug/wpavatar/wpdate/wpfonts/wpmind 共 11 场景
- play-deploy 同步目标增加 screenshots 目录
- 新增 play-capture 自动截图脚本

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-02 19:40:23 +08:00

173 lines
6 KiB
JavaScript
Executable file

#!/usr/bin/env node
// play-capture — 标准化产品截图生成工具
// 用法: play-capture <plugin-id> [--all] [--watermark] [--webp]
// 产出: ~/play/screenshots/{plugin-id}/ 下的 PNG + WebP 截图
const { chromium } = require('playwright');
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const SITE = 'https://play.wenpai.net';
const PLUGINS_JSON = path.join(process.env.HOME, 'play/plugins.json');
const BLUEPRINTS_DIR = path.join(process.env.HOME, 'play/blueprints');
const SCREENSHOTS_DIR = path.join(process.env.HOME, 'play/screenshots');
const args = process.argv.slice(2);
const doAll = args.includes('--all');
const doWatermark = args.includes('--watermark');
const doWebp = args.includes('--webp');
const pluginId = args.find(a => !a.startsWith('--'));
if (!pluginId && !doAll) {
console.log('用法: play-capture <plugin-id> [--all] [--watermark] [--webp]');
console.log(' play-capture wpslug 单个插件截图');
console.log(' play-capture --all 所有插件截图');
console.log(' play-capture wpslug --watermark 加水印');
console.log(' play-capture --all --webp 转 WebP');
process.exit(0);
}
// 每个插件的截图场景定义
const SCENES = {
wpslug: [
{ id: 'settings', name: '设置页', path: '/wp-admin/options-general.php?page=wpslug' },
{ id: 'plugins', name: '插件列表', path: '/wp-admin/plugins.php' },
{ id: 'editor', name: '编辑器', path: '/wp-admin/post-new.php', setup: async (frame) => {
// 关闭欢迎弹窗
const closeBtn = await frame.$('.components-modal__header button, [aria-label="Close"], [aria-label="关闭"]');
if (closeBtn) await closeBtn.click();
await frame.waitForTimeout(500);
// 输入中文标题触发 slug 生成
const titleInput = await frame.$('[aria-label="添加标题"], [aria-label="Add title"], .editor-post-title__input');
if (titleInput) {
await titleInput.click();
await titleInput.type('测试中文标题自动转拼音', { delay: 30 });
await frame.waitForTimeout(2000);
}
}},
],
wpavatar: [
{ id: 'settings', name: '设置页', path: '/wp-admin/admin.php?page=wpavatar-settings' },
{ id: 'plugins', name: '插件列表', path: '/wp-admin/plugins.php' },
{ id: 'discussion', name: '评论设置', path: '/wp-admin/options-discussion.php' },
],
wpdate: [
{ id: 'settings', name: '日历页', path: '/wp-admin/admin.php?page=wpdate-calendar' },
{ id: 'plugins', name: '插件列表', path: '/wp-admin/plugins.php' },
],
wpfonts: [
{ id: 'plugins', name: '插件列表', path: '/wp-admin/plugins.php' },
],
wpmind: [
{ id: 'settings', name: '主面板', path: '/wp-admin/admin.php?page=wpmind' },
{ id: 'plugins', name: '插件列表', path: '/wp-admin/plugins.php' },
],
};
function getPlugins() {
const data = JSON.parse(fs.readFileSync(PLUGINS_JSON, 'utf8'));
return data.filter(p => p.group === 'plugin');
}
async function capturePlugin(browser, pluginId) {
const outDir = path.join(SCREENSHOTS_DIR, pluginId);
fs.mkdirSync(outDir, { recursive: true });
const scenes = SCENES[pluginId];
if (!scenes) {
console.log(` [!] 未定义截图场景: ${pluginId}`);
return [];
}
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
const results = [];
try {
// 加载 Playground
await page.goto(`${SITE}/?plugin=${pluginId}`, { waitUntil: 'load', timeout: 60000 });
await page.waitForTimeout(35000);
// 找到 WordPress iframe
let wpFrame = null;
for (const frame of page.frames()) {
const url = frame.url();
if (url.includes('scope:') || (url.includes('wp-admin') && !url.includes('playground.html'))) {
wpFrame = frame;
break;
}
}
if (!wpFrame) {
console.log(' [!] 未找到 WordPress iframe');
await page.close();
return [];
}
// 逐场景截图
for (const scene of scenes) {
try {
// 导航到目标页
await wpFrame.evaluate((p) => { window.location.href = p; }, scene.path);
await wpFrame.waitForLoadState('load').catch(() => {});
await wpFrame.waitForTimeout(3000);
// 执行场景特定设置
if (scene.setup) {
await scene.setup(wpFrame);
}
// 截图(整个页面,包含 Playground 外壳)
const pngPath = path.join(outDir, `${pluginId}-${scene.id}.png`);
await page.screenshot({ path: pngPath });
results.push({ scene: scene.id, name: scene.name, path: pngPath });
console.log(` [+] ${scene.name}: ${pngPath}`);
// 后处理
const imgArgs = ['--resize', '1280'];
if (doWatermark) imgArgs.push('--watermark', 'play.wenpai.net');
if (doWebp) imgArgs.push('--webp');
try {
execSync(`img-process "${pngPath}" ${imgArgs.join(' ')}`, { stdio: 'pipe' });
} catch (e) {
// img-process 可能不可用,静默跳过
}
} catch (err) {
console.log(` [!] ${scene.name} 失败: ${err.message.substring(0, 80)}`);
}
}
} catch (err) {
console.log(` [!] 加载失败: ${err.message.substring(0, 80)}`);
} finally {
await page.close();
}
return results;
}
(async () => {
const browser = await chromium.launch({ headless: true });
const plugins = doAll ? getPlugins().map(p => p.id) : [pluginId];
const allResults = {};
for (const id of plugins) {
process.stdout.write(`\n截图 ${id} ...\n`);
const results = await capturePlugin(browser, id);
allResults[id] = results;
}
await browser.close();
// 汇总
const total = Object.values(allResults).reduce((sum, r) => sum + r.length, 0);
console.log(`\n=== 完成: ${total} 张截图, ${plugins.length} 个插件 ===`);
console.log(`目录: ${SCREENSHOTS_DIR}`);
// 保存清单
fs.writeFileSync(
path.join(SCREENSHOTS_DIR, 'manifest.json'),
JSON.stringify(allResults, null, 2)
);
})();