From 10820595cf6cbd41f8b2a65b9eb09dc330fce048 Mon Sep 17 00:00:00 2001 From: kris Date: Fri, 20 Mar 2026 15:14:12 +0800 Subject: [PATCH] fix: harden douyin browser capture persistence --- .../capture_and_sync.mjs | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/scripts/douyin-browser-capture/capture_and_sync.mjs b/scripts/douyin-browser-capture/capture_and_sync.mjs index 6116370..0c45c15 100644 --- a/scripts/douyin-browser-capture/capture_and_sync.mjs +++ b/scripts/douyin-browser-capture/capture_and_sync.mjs @@ -504,6 +504,14 @@ async function saveJson(filePath, value) { await fs.writeFile(filePath, JSON.stringify(value, null, 2), "utf8"); } +async function saveJsonSafe(filePath, value) { + try { + await saveJson(filePath, value); + } catch (error) { + console.error(`Failed to write ${filePath}: ${error?.message || error}`); + } +} + async function loginStoryForge(baseUrl, username, password) { const response = await fetch(`${baseUrl.replace(/\/$/, "")}/v2/auth/login`, { method: "POST", @@ -538,7 +546,7 @@ async function captureCreatorPages(context, options, runDir) { return pages; } - for (const url of options.creatorCenterUrls) { + for (const [index, url] of options.creatorCenterUrls.entries()) { const page = await context.newPage(); const responseCapture = await createResponseCapture(page); try { @@ -551,7 +559,7 @@ async function captureCreatorPages(context, options, runDir) { payload: bundle }); await saveJson( - path.join(runDir, `creator-${sanitizeName(bundle.page_title || bundle.page_url)}.json`), + path.join(runDir, `creator-${String(index + 1).padStart(2, "0")}-${sanitizeName(bundle.page_title || bundle.page_url)}.json`), bundle ); } finally { @@ -603,6 +611,17 @@ async function main() { await ensureDir(runDir); await ensureDir(options.stateDir); + const summary = { + profile_url: options.profileUrl, + output_dir: runDir, + video_link_count: 0, + captured_video_pages: 0, + captured_creator_pages: 0, + sync_enabled: options.syncEnabled, + status: "running" + }; + await saveJsonSafe(path.join(runDir, "summary.json"), summary); + const context = await chromium.launchPersistentContext(options.stateDir, { headless: options.headless, viewport: { width: 1440, height: 1024 }, @@ -638,14 +657,9 @@ async function main() { }; await saveJson(path.join(runDir, "storyforge-sync-request.json"), syncBody); - const summary = { - profile_url: options.profileUrl, - output_dir: runDir, - video_link_count: videoLinks.length, - captured_video_pages: videoPages.length, - captured_creator_pages: creatorPages.length, - sync_enabled: options.syncEnabled - }; + summary.video_link_count = videoLinks.length; + summary.captured_video_pages = videoPages.length; + summary.captured_creator_pages = creatorPages.length; if (options.syncEnabled) { let token = options.storyforgeToken; @@ -670,8 +684,17 @@ async function main() { await saveJson(path.join(runDir, "storyforge-sync-response.json"), workspace); } + summary.status = "completed"; await saveJson(path.join(runDir, "summary.json"), summary); console.log(JSON.stringify(summary, null, 2)); + } catch (error) { + summary.status = "failed"; + summary.error = error?.stack || String(error); + await saveJsonSafe(path.join(runDir, "summary.json"), summary); + await saveJsonSafe(path.join(runDir, "storyforge-sync-error.json"), { + error: error?.stack || String(error) + }); + throw error; } finally { await context.close().catch(() => {}); }