11
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
Reference in New Issue
Block a user