From 22f24a565c6f174965125f67c8dcb63a5d16130c Mon Sep 17 00:00:00 2001 From: "949036910@qq.com" <> Date: Fri, 29 May 2026 00:42:58 +0800 Subject: [PATCH] 11 --- scripts/nodejs/douyin_browser_manager.js | 22 +++++++++++++++++++--- scripts/nodejs/douyin_video_downloader.js | 22 +++++++++++++++++----- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/scripts/nodejs/douyin_browser_manager.js b/scripts/nodejs/douyin_browser_manager.js index 2221748..788d17b 100644 --- a/scripts/nodejs/douyin_browser_manager.js +++ b/scripts/nodejs/douyin_browser_manager.js @@ -73,7 +73,11 @@ class DouyinBrowserManager { constructor() { this.browser = null; this.isInitializing = false; - this.userDataPath = getRuntimeDataPath("browser-data", "douyin"); + // 下载链路使用独立临时 profile,避免与其他流程/遗留进程抢占同一目录锁。 + this.userDataPath = getRuntimeDataPath( + "temp", + `douyin-pipeline-${process.pid}-${Date.now()}` + ); } static getInstance() { @@ -180,8 +184,10 @@ class DouyinBrowserManager { const page = await this.browser.newPage(); await page.setUserAgent(DEFAULT_USER_AGENT); await page.evaluateOnNewDocument(STEALTH_INIT_SCRIPT); - page.setDefaultNavigationTimeout(60000); - page.setDefaultTimeout(60000); + const pageTimeoutMs = + Number(process.env.AICLIENT_DOUYIN_PAGE_TIMEOUT_MS) || 90_000; + page.setDefaultNavigationTimeout(pageTimeoutMs); + page.setDefaultTimeout(pageTimeoutMs); return page; } @@ -192,6 +198,16 @@ class DouyinBrowserManager { this.browser = null; console.log("浏览器管理器已关闭"); } + if ( + this.userDataPath && + this.userDataPath.includes(`${path.sep}temp${path.sep}douyin-pipeline-`) + ) { + try { + fs.rmSync(this.userDataPath, { recursive: true, force: true }); + } catch { + /* ignore temp dir cleanup errors */ + } + } } catch (error) { console.error("关闭浏览器管理器失败:", error); } diff --git a/scripts/nodejs/douyin_video_downloader.js b/scripts/nodejs/douyin_video_downloader.js index f5520bb..85d885d 100644 --- a/scripts/nodejs/douyin_video_downloader.js +++ b/scripts/nodejs/douyin_video_downloader.js @@ -15,6 +15,16 @@ const { DouyinBrowserManager, getRuntimeDataPath } = require("./douyin_browser_m const DIRECT_VIDEO_PATTERN = /https?:\/\/[^\s"'<>]+?\.(mp4|mov|m4v|webm|avi|mkv)(\?[^\s"'<>]*)?/i; +/** 视频页导航超时(毫秒),可通过 AICLIENT_DOUYIN_PAGE_TIMEOUT_MS 覆盖 */ +const PAGE_NAV_TIMEOUT_MS = + Number(process.env.AICLIENT_DOUYIN_PAGE_TIMEOUT_MS) || 60_000; +/** 等待 aweme detail API 拦截的超时(毫秒) */ +const API_INTERCEPT_TIMEOUT_MS = + Number(process.env.AICLIENT_DOUYIN_API_WAIT_MS) || 120_000; +/** 打开视频页后额外等待,便于接口与播放器初始化 */ +const PAGE_SETTLE_MS = + Number(process.env.AICLIENT_DOUYIN_PAGE_SETTLE_MS) || 5_000; + const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); class DouyinVideoDownloader { @@ -214,7 +224,7 @@ class DouyinVideoDownloader { console.log("API 拦截超时,将尝试其他方法"); resolve(null); } - }, 30000); + }, API_INTERCEPT_TIMEOUT_MS); }); } @@ -287,8 +297,9 @@ class DouyinVideoDownloader { try { await page.goto(directVideoUrl, { waitUntil: "domcontentloaded", - timeout: 30000, + timeout: PAGE_NAV_TIMEOUT_MS, }); + await sleep(PAGE_SETTLE_MS); const currentUrl = page.url(); const isLoginPage = currentUrl.includes("/login") || currentUrl.includes("login?"); @@ -297,8 +308,9 @@ class DouyinVideoDownloader { await this.ensureLoggedIn(page); await page.goto(directVideoUrl, { waitUntil: "domcontentloaded", - timeout: 30000, + timeout: PAGE_NAV_TIMEOUT_MS, }); + await sleep(PAGE_SETTLE_MS); if (page.url().includes("/login")) { return { success: false, @@ -405,9 +417,9 @@ class DouyinVideoDownloader { await page.goto("https://www.douyin.com/", { waitUntil: "domcontentloaded", - timeout: 30000, + timeout: PAGE_NAV_TIMEOUT_MS, }); - await sleep(2000); + await sleep(PAGE_SETTLE_MS); const isLoggedIn = await page.evaluate(() => { const loginBtnSelectors = [