This commit is contained in:
949036910@qq.com
2026-05-29 00:42:58 +08:00
parent e6dec231ea
commit 22f24a565c
2 changed files with 36 additions and 8 deletions

View File

@@ -73,7 +73,11 @@ class DouyinBrowserManager {
constructor() { constructor() {
this.browser = null; this.browser = null;
this.isInitializing = false; this.isInitializing = false;
this.userDataPath = getRuntimeDataPath("browser-data", "douyin"); // 下载链路使用独立临时 profile避免与其他流程/遗留进程抢占同一目录锁。
this.userDataPath = getRuntimeDataPath(
"temp",
`douyin-pipeline-${process.pid}-${Date.now()}`
);
} }
static getInstance() { static getInstance() {
@@ -180,8 +184,10 @@ class DouyinBrowserManager {
const page = await this.browser.newPage(); const page = await this.browser.newPage();
await page.setUserAgent(DEFAULT_USER_AGENT); await page.setUserAgent(DEFAULT_USER_AGENT);
await page.evaluateOnNewDocument(STEALTH_INIT_SCRIPT); await page.evaluateOnNewDocument(STEALTH_INIT_SCRIPT);
page.setDefaultNavigationTimeout(60000); const pageTimeoutMs =
page.setDefaultTimeout(60000); Number(process.env.AICLIENT_DOUYIN_PAGE_TIMEOUT_MS) || 90_000;
page.setDefaultNavigationTimeout(pageTimeoutMs);
page.setDefaultTimeout(pageTimeoutMs);
return page; return page;
} }
@@ -192,6 +198,16 @@ class DouyinBrowserManager {
this.browser = null; this.browser = null;
console.log("浏览器管理器已关闭"); 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) { } catch (error) {
console.error("关闭浏览器管理器失败:", error); console.error("关闭浏览器管理器失败:", error);
} }

View File

@@ -15,6 +15,16 @@ const { DouyinBrowserManager, getRuntimeDataPath } = require("./douyin_browser_m
const DIRECT_VIDEO_PATTERN = const DIRECT_VIDEO_PATTERN =
/https?:\/\/[^\s"'<>]+?\.(mp4|mov|m4v|webm|avi|mkv)(\?[^\s"'<>]*)?/i; /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)); const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
class DouyinVideoDownloader { class DouyinVideoDownloader {
@@ -214,7 +224,7 @@ class DouyinVideoDownloader {
console.log("API 拦截超时,将尝试其他方法"); console.log("API 拦截超时,将尝试其他方法");
resolve(null); resolve(null);
} }
}, 30000); }, API_INTERCEPT_TIMEOUT_MS);
}); });
} }
@@ -287,8 +297,9 @@ class DouyinVideoDownloader {
try { try {
await page.goto(directVideoUrl, { await page.goto(directVideoUrl, {
waitUntil: "domcontentloaded", waitUntil: "domcontentloaded",
timeout: 30000, timeout: PAGE_NAV_TIMEOUT_MS,
}); });
await sleep(PAGE_SETTLE_MS);
const currentUrl = page.url(); const currentUrl = page.url();
const isLoginPage = const isLoginPage =
currentUrl.includes("/login") || currentUrl.includes("login?"); currentUrl.includes("/login") || currentUrl.includes("login?");
@@ -297,8 +308,9 @@ class DouyinVideoDownloader {
await this.ensureLoggedIn(page); await this.ensureLoggedIn(page);
await page.goto(directVideoUrl, { await page.goto(directVideoUrl, {
waitUntil: "domcontentloaded", waitUntil: "domcontentloaded",
timeout: 30000, timeout: PAGE_NAV_TIMEOUT_MS,
}); });
await sleep(PAGE_SETTLE_MS);
if (page.url().includes("/login")) { if (page.url().includes("/login")) {
return { return {
success: false, success: false,
@@ -405,9 +417,9 @@ class DouyinVideoDownloader {
await page.goto("https://www.douyin.com/", { await page.goto("https://www.douyin.com/", {
waitUntil: "domcontentloaded", waitUntil: "domcontentloaded",
timeout: 30000, timeout: PAGE_NAV_TIMEOUT_MS,
}); });
await sleep(2000); await sleep(PAGE_SETTLE_MS);
const isLoggedIn = await page.evaluate(() => { const isLoggedIn = await page.evaluate(() => {
const loginBtnSelectors = [ const loginBtnSelectors = [