diff --git a/README.md b/README.md index 88d08f0..d6d657a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,19 @@ cd /Users/kris/code/StoryForge-gitea/android-app cd /Users/kris/code/StoryForge-gitea/scripts/douyin-browser-capture npm install npx playwright install chromium +npm run control-panel +``` + +打开: + +```text +http://127.0.0.1:3618 +``` + +或者继续用命令行: + +```bash +cd /Users/kris/code/StoryForge-gitea/scripts/douyin-browser-capture npm run capture -- \ --profile-url https://www.douyin.com/user/your_account \ --storyforge-username kris \ @@ -35,6 +48,8 @@ npm run capture -- \ - 这是“真实浏览器 + 人工登录/过挑战 + 自动提取 + 回写 StoryForge”的辅助采集工具 - 默认输出到 `output/playwright/douyin/` +- 本地控制台模式会把每次运行保存到 `output/playwright/douyin/control-panel/` +- 控制台支持“开始采集 -> 浏览器登录 -> 网页点继续 -> 自动同步”的点击式流程 - 详细说明见 `scripts/douyin-browser-capture/README.md` ## Collector Service diff --git a/docs/AUDIT_2026-03-18.md b/docs/AUDIT_2026-03-18.md index 241527a..0fb2694 100644 --- a/docs/AUDIT_2026-03-18.md +++ b/docs/AUDIT_2026-03-18.md @@ -117,8 +117,10 @@ - 真实 smoke 结果表明,纯 public 主页抓取会落到 `byted_acrawler` 挑战页,而不是正常 profile 数据页 - 同时,`manual_profile_payload + manual_work_payloads` 已验证可完成账号入库、分析报告生成、相似账号搜索和对标关系写入 - 现已新增浏览器辅助采集工具 `/Users/kris/code/StoryForge-gitea/scripts/douyin-browser-capture/capture_and_sync.mjs` +- 同目录现已新增本地控制台 `/Users/kris/code/StoryForge-gitea/scripts/douyin-browser-capture/control_panel.mjs` - 该工具使用真实 Playwright Chromium 会话打开抖音页面,允许人工登录 / 过滑块后继续自动提取 ` + +`; +} + +const server = http.createServer(async (req, res) => { + const url = new URL(req.url || "/", "http://127.0.0.1"); + try { + if (req.method === "GET" && url.pathname === "/") { + sendHtml(res, renderPage()); + return; + } + if (req.method === "GET" && url.pathname === "/api/status") { + const activeRun = getActiveRun(); + if (activeRun) { + await refreshRunArtifacts(activeRun); + } + sendJson(res, 200, { + activeRun: serializeRun(activeRun), + recentRuns: await listRecentRuns() + }); + return; + } + if (req.method === "POST" && url.pathname === "/api/start") { + const payload = await readJsonBody(req); + const run = await startRun(payload); + sendJson(res, 200, { run: serializeRun(run) }); + return; + } + if (req.method === "POST" && /^\/api\/runs\/[^/]+\/continue$/.test(url.pathname)) { + const runId = decodeURIComponent(url.pathname.split("/")[3] || ""); + const run = await continueRun(runId); + sendJson(res, 200, { run: serializeRun(run) }); + return; + } + sendJson(res, 404, { error: "Not found" }); + } catch (error) { + sendJson(res, 500, { error: error?.message || String(error) }); + } +}); + +ensureDir(DEFAULT_OUTPUT_ROOT) + .then(() => { + server.listen(DEFAULT_PORT, "127.0.0.1", () => { + console.log(`StoryForge Douyin control panel: http://127.0.0.1:${DEFAULT_PORT}`); + }); + }) + .catch((error) => { + console.error(error?.stack || String(error)); + process.exitCode = 1; + }); diff --git a/scripts/douyin-browser-capture/package.json b/scripts/douyin-browser-capture/package.json index 559fc51..d0a5cd7 100644 --- a/scripts/douyin-browser-capture/package.json +++ b/scripts/douyin-browser-capture/package.json @@ -6,6 +6,7 @@ "description": "Browser-assisted Douyin capture and sync tool for StoryForge", "scripts": { "capture": "node ./capture_and_sync.mjs", + "control-panel": "node ./control_panel.mjs", "help": "node ./capture_and_sync.mjs --help" }, "dependencies": {