douyin-downloader 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -0
- package/dist/bin/dydl.d.ts +2 -0
- package/dist/bin/dydl.js +17 -0
- package/dist/bin/dydl.js.map +1 -0
- package/dist/src/cli/commands/check.d.ts +10 -0
- package/dist/src/cli/commands/check.js +102 -0
- package/dist/src/cli/commands/check.js.map +1 -0
- package/dist/src/cli/commands/info.d.ts +10 -0
- package/dist/src/cli/commands/info.js +106 -0
- package/dist/src/cli/commands/info.js.map +1 -0
- package/dist/src/cli/commands/list.d.ts +10 -0
- package/dist/src/cli/commands/list.js +85 -0
- package/dist/src/cli/commands/list.js.map +1 -0
- package/dist/src/cli/commands/login.d.ts +10 -0
- package/dist/src/cli/commands/login.js +158 -0
- package/dist/src/cli/commands/login.js.map +1 -0
- package/dist/src/cli/commands/open.d.ts +10 -0
- package/dist/src/cli/commands/open.js +141 -0
- package/dist/src/cli/commands/open.js.map +1 -0
- package/dist/src/cli/commands/video.d.ts +10 -0
- package/dist/src/cli/commands/video.js +209 -0
- package/dist/src/cli/commands/video.js.map +1 -0
- package/dist/src/cli/index.d.ts +15 -0
- package/dist/src/cli/index.js +134 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/progress-display.d.ts +80 -0
- package/dist/src/cli/progress-display.js +225 -0
- package/dist/src/cli/progress-display.js.map +1 -0
- package/dist/src/cli/utils.d.ts +31 -0
- package/dist/src/cli/utils.js +171 -0
- package/dist/src/cli/utils.js.map +1 -0
- package/dist/src/douyin/auth/cookie-path.d.ts +22 -0
- package/dist/src/douyin/auth/cookie-path.js +72 -0
- package/dist/src/douyin/auth/cookie-path.js.map +1 -0
- package/dist/src/douyin/auth/cookie-storage.d.ts +19 -0
- package/dist/src/douyin/auth/cookie-storage.js +65 -0
- package/dist/src/douyin/auth/cookie-storage.js.map +1 -0
- package/dist/src/douyin/auth/errors.d.ts +28 -0
- package/dist/src/douyin/auth/errors.js +49 -0
- package/dist/src/douyin/auth/errors.js.map +1 -0
- package/dist/src/douyin/auth/getDefaultCookiePath.d.ts +24 -0
- package/dist/src/douyin/auth/getDefaultCookiePath.js +90 -0
- package/dist/src/douyin/auth/getDefaultCookiePath.js.map +1 -0
- package/dist/src/douyin/auth/index.d.ts +16 -0
- package/dist/src/douyin/auth/index.js +68 -0
- package/dist/src/douyin/auth/index.js.map +1 -0
- package/dist/src/douyin/auth/isValidCookie.d.ts +18 -0
- package/dist/src/douyin/auth/isValidCookie.js +60 -0
- package/dist/src/douyin/auth/isValidCookie.js.map +1 -0
- package/dist/src/douyin/auth/loadAndValidateCookie.d.ts +17 -0
- package/dist/src/douyin/auth/loadAndValidateCookie.js +45 -0
- package/dist/src/douyin/auth/loadAndValidateCookie.js.map +1 -0
- package/dist/src/douyin/auth/loadCookie.d.ts +17 -0
- package/dist/src/douyin/auth/loadCookie.js +79 -0
- package/dist/src/douyin/auth/loadCookie.js.map +1 -0
- package/dist/src/douyin/auth/login.d.ts +33 -0
- package/dist/src/douyin/auth/login.js +157 -0
- package/dist/src/douyin/auth/login.js.map +1 -0
- package/dist/src/douyin/auth/saveCookie.d.ts +17 -0
- package/dist/src/douyin/auth/saveCookie.js +89 -0
- package/dist/src/douyin/auth/saveCookie.js.map +1 -0
- package/dist/src/douyin/auth/validate.d.ts +11 -0
- package/dist/src/douyin/auth/validate.js +104 -0
- package/dist/src/douyin/auth/validate.js.map +1 -0
- package/dist/src/douyin/browser/manager.d.ts +54 -0
- package/dist/src/douyin/browser/manager.js +344 -0
- package/dist/src/douyin/browser/manager.js.map +1 -0
- package/dist/src/douyin/download/download-manager.d.ts +25 -0
- package/dist/src/douyin/download/download-manager.js +107 -0
- package/dist/src/douyin/download/download-manager.js.map +1 -0
- package/dist/src/douyin/download/error-handler.d.ts +49 -0
- package/dist/src/douyin/download/error-handler.js +160 -0
- package/dist/src/douyin/download/error-handler.js.map +1 -0
- package/dist/src/douyin/download/index.d.ts +39 -0
- package/dist/src/douyin/download/index.js +156 -0
- package/dist/src/douyin/download/index.js.map +1 -0
- package/dist/src/douyin/download/path-formatter.d.ts +42 -0
- package/dist/src/douyin/download/path-formatter.js +107 -0
- package/dist/src/douyin/download/path-formatter.js.map +1 -0
- package/dist/src/douyin/download/video-downloader.d.ts +35 -0
- package/dist/src/douyin/download/video-downloader.js +223 -0
- package/dist/src/douyin/download/video-downloader.js.map +1 -0
- package/dist/src/douyin/index.d.ts +19 -0
- package/dist/src/douyin/index.js +52 -0
- package/dist/src/douyin/index.js.map +1 -0
- package/dist/src/douyin/info/batch-processor.d.ts +15 -0
- package/dist/src/douyin/info/batch-processor.js +65 -0
- package/dist/src/douyin/info/batch-processor.js.map +1 -0
- package/dist/src/douyin/info/browser-manager.d.ts +56 -0
- package/dist/src/douyin/info/browser-manager.js +225 -0
- package/dist/src/douyin/info/browser-manager.js.map +1 -0
- package/dist/src/douyin/info/error-handler.d.ts +36 -0
- package/dist/src/douyin/info/error-handler.js +172 -0
- package/dist/src/douyin/info/error-handler.js.map +1 -0
- package/dist/src/douyin/info/fetch-video-detail.d.ts +14 -0
- package/dist/src/douyin/info/fetch-video-detail.js +247 -0
- package/dist/src/douyin/info/fetch-video-detail.js.map +1 -0
- package/dist/src/douyin/info/index.d.ts +29 -0
- package/dist/src/douyin/info/index.js +85 -0
- package/dist/src/douyin/info/index.js.map +1 -0
- package/dist/src/douyin/info/text-processor.d.ts +15 -0
- package/dist/src/douyin/info/text-processor.js +47 -0
- package/dist/src/douyin/info/text-processor.js.map +1 -0
- package/dist/src/douyin/info/user.d.ts +26 -0
- package/dist/src/douyin/info/user.js +237 -0
- package/dist/src/douyin/info/user.js.map +1 -0
- package/dist/src/douyin/parser/containsDouyinLink.d.ts +18 -0
- package/dist/src/douyin/parser/containsDouyinLink.js +27 -0
- package/dist/src/douyin/parser/containsDouyinLink.js.map +1 -0
- package/dist/src/douyin/parser/extract-links.d.ts +23 -0
- package/dist/src/douyin/parser/extract-links.js +79 -0
- package/dist/src/douyin/parser/extract-links.js.map +1 -0
- package/dist/src/douyin/parser/extractDouyinLinks.d.ts +18 -0
- package/dist/src/douyin/parser/extractDouyinLinks.js +58 -0
- package/dist/src/douyin/parser/extractDouyinLinks.js.map +1 -0
- package/dist/src/douyin/parser/index.d.ts +35 -0
- package/dist/src/douyin/parser/index.js +70 -0
- package/dist/src/douyin/parser/index.js.map +1 -0
- package/dist/src/douyin/parser/link-patterns.d.ts +34 -0
- package/dist/src/douyin/parser/link-patterns.js +121 -0
- package/dist/src/douyin/parser/link-patterns.js.map +1 -0
- package/dist/src/douyin/parser/parse-batch.d.ts +26 -0
- package/dist/src/douyin/parser/parse-batch.js +67 -0
- package/dist/src/douyin/parser/parse-batch.js.map +1 -0
- package/dist/src/douyin/parser/parseDouyinLinks.d.ts +30 -0
- package/dist/src/douyin/parser/parseDouyinLinks.js +164 -0
- package/dist/src/douyin/parser/parseDouyinLinks.js.map +1 -0
- package/dist/src/douyin/parser/resolve-links.d.ts +25 -0
- package/dist/src/douyin/parser/resolve-links.js +131 -0
- package/dist/src/douyin/parser/resolve-links.js.map +1 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +72 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types.d.ts +217 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/browser.d.ts +73 -0
- package/dist/src/utils/browser.js +96 -0
- package/dist/src/utils/browser.js.map +1 -0
- package/dist/src/utils/error.d.ts +160 -0
- package/dist/src/utils/error.js +334 -0
- package/dist/src/utils/error.js.map +1 -0
- package/dist/src/utils/fetch.d.ts +41 -0
- package/dist/src/utils/fetch.js +155 -0
- package/dist/src/utils/fetch.js.map +1 -0
- package/dist/src/utils/file.d.ts +46 -0
- package/dist/src/utils/file.js +189 -0
- package/dist/src/utils/file.js.map +1 -0
- package/dist/src/utils/index.d.ts +11 -0
- package/dist/src/utils/index.js +29 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/logger.d.ts +161 -0
- package/dist/src/utils/logger.js +286 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/performance.d.ts +98 -0
- package/dist/src/utils/performance.js +292 -0
- package/dist/src/utils/performance.js.map +1 -0
- package/dist/src/utils/retry.d.ts +56 -0
- package/dist/src/utils/retry.js +127 -0
- package/dist/src/utils/retry.js.map +1 -0
- package/package.json +61 -0
@@ -0,0 +1,344 @@
|
|
1
|
+
"use strict";
|
2
|
+
// src/douyin/browser/manager.ts
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
5
|
+
};
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
7
|
+
exports.launchBrowser = launchBrowser;
|
8
|
+
exports.newPage = newPage;
|
9
|
+
exports.setCookiesOnPage = setCookiesOnPage;
|
10
|
+
exports.closeBrowser = closeBrowser;
|
11
|
+
exports.launchLoginBrowser = launchLoginBrowser;
|
12
|
+
const puppeteer_extra_1 = __importDefault(require("puppeteer-extra"));
|
13
|
+
const puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth"));
|
14
|
+
const logger_1 = require("../../utils/logger"); // 確保路徑正確
|
15
|
+
const env_paths_1 = __importDefault(require("env-paths")); // 匯入 env-paths
|
16
|
+
const path_1 = __importDefault(require("path")); // 匯入 path 模組
|
17
|
+
const fs_1 = __importDefault(require("fs")); // 匯入 fs 模組
|
18
|
+
// 應用 stealth plugin (理想情況下應在應用程式入口點執行一次)
|
19
|
+
// 暫時放在這裡以保持模組封裝性
|
20
|
+
// 使用 !puppeteer.plugins.some(...) 檢查確保只應用一次
|
21
|
+
if (!puppeteer_extra_1.default.plugins.find((p) => p.name === 'stealth')) {
|
22
|
+
puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)());
|
23
|
+
logger_1.utilsLogger.debug('Stealth plugin applied to puppeteer-extra in browser manager.');
|
24
|
+
}
|
25
|
+
// --- 預設 User Data Directory ---
|
26
|
+
// 定義應用程式名稱
|
27
|
+
const APP_NAME = 'douyin-downloader';
|
28
|
+
// 獲取標準路徑
|
29
|
+
const paths = (0, env_paths_1.default)(APP_NAME, { suffix: '' }); // suffix: '' 避免額外的 'nodejs' 路徑
|
30
|
+
// 建立 Puppeteer 專用的資料目錄路徑
|
31
|
+
const puppeteerUserDataPath = path_1.default.join(paths.data, 'puppeteer');
|
32
|
+
// 確保目錄存在
|
33
|
+
try {
|
34
|
+
fs_1.default.mkdirSync(puppeteerUserDataPath, { recursive: true });
|
35
|
+
logger_1.utilsLogger.debug(`預設 Puppeteer User Data Directory 設定為: ${puppeteerUserDataPath}`);
|
36
|
+
}
|
37
|
+
catch (error) {
|
38
|
+
logger_1.utilsLogger.error(`無法創建預設 Puppeteer User Data Directory (${puppeteerUserDataPath}):`, error);
|
39
|
+
// 如果無法創建,則不設定預設值,讓 Puppeteer 使用其內部預設
|
40
|
+
}
|
41
|
+
/**
|
42
|
+
* 啟動一個新的 Puppeteer 瀏覽器實例。
|
43
|
+
* @param options 啟動選項
|
44
|
+
* @returns Promise<Browser>
|
45
|
+
*/
|
46
|
+
async function launchBrowser(options = {}) {
|
47
|
+
logger_1.utilsLogger.debug('啟動新的 Puppeteer 瀏覽器實例...', options);
|
48
|
+
let headlessOption;
|
49
|
+
if (options.headless === 'shell') {
|
50
|
+
headlessOption = 'shell';
|
51
|
+
}
|
52
|
+
else if (options.headless === false) {
|
53
|
+
headlessOption = false;
|
54
|
+
}
|
55
|
+
else {
|
56
|
+
// 預設為新版無頭模式 (如果未定義或為 true)
|
57
|
+
// 注意: @types/puppeteer 可能不直接支援 'new' 字面值,使用 true 作為替代
|
58
|
+
// 預設為新版無頭模式 (如果未定義或為 true)
|
59
|
+
// 注意: @types/puppeteer 可能不直接支援 'new' 字面值,使用 true 作為替代
|
60
|
+
// headlessOption = true; // Keep previous logic for reference
|
61
|
+
// Match reference/DouyinParser.ts: force non-headless for login, allow override?
|
62
|
+
// For launchBrowser (general purpose), let's keep the flexible option
|
63
|
+
// For launchLoginBrowser, it forces headless: false
|
64
|
+
// Map 'new' to true, otherwise use the provided value or default undefined to true.
|
65
|
+
if (options.headless === 'new') {
|
66
|
+
headlessOption = true; // Map 'new' to classic headless (true)
|
67
|
+
logger_1.utilsLogger.debug("Mapping headless: 'new' to headless: true");
|
68
|
+
}
|
69
|
+
else {
|
70
|
+
// Assign 'shell', false, true directly. Default undefined to true.
|
71
|
+
headlessOption = options.headless === undefined ? true : options.headless;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
// Revert to more comprehensive args, keeping lang setting
|
75
|
+
const defaultArgs = [
|
76
|
+
'--no-sandbox',
|
77
|
+
'--disable-setuid-sandbox',
|
78
|
+
'--disable-dev-shm-usage', // Crucial for Docker/CI environments
|
79
|
+
// '--disable-accelerated-2d-canvas',
|
80
|
+
'--no-first-run',
|
81
|
+
'--no-zygote',
|
82
|
+
// '--disable-gpu', // Often needed in headless environments
|
83
|
+
// '--disable-extensions',
|
84
|
+
'--disable-background-networking',
|
85
|
+
'--disable-background-timer-throttling',
|
86
|
+
// '--disable-popup-blocking',
|
87
|
+
'--disable-hang-monitor',
|
88
|
+
'--disable-ipc-flooding-protection',
|
89
|
+
'--disable-renderer-backgrounding',
|
90
|
+
'--enable-automation',
|
91
|
+
'--disable-infobars', // Hide "Chrome is controlled by automated test software"
|
92
|
+
'--disable-blink-features=AutomationControlled', // Further hide automation flags
|
93
|
+
'--window-size=1920,1080', // Set window size via args
|
94
|
+
'--lang=zh-CN' // Keep language setting
|
95
|
+
];
|
96
|
+
if (!options.userDataDir)
|
97
|
+
logger_1.utilsLogger.debug('未提供 userDataDir,將使用預設路徑:', puppeteerUserDataPath);
|
98
|
+
const launchOptions = {
|
99
|
+
headless: headlessOption,
|
100
|
+
executablePath: options.executablePath,
|
101
|
+
// 優先使用選項中提供的 userDataDir,否則使用 env-paths 計算出的預設路徑 (如果成功創建)
|
102
|
+
userDataDir: options.userDataDir ?? (fs_1.default.existsSync(puppeteerUserDataPath) ? puppeteerUserDataPath : undefined),
|
103
|
+
args: [...defaultArgs, ...(options.args || [])], // Combine default and custom args
|
104
|
+
defaultViewport: { width: 1920, height: 1080 }, // Keep null when using window-size arg
|
105
|
+
timeout: options.timeout || 60000,
|
106
|
+
env: {
|
107
|
+
...process.env, // Inherit existing env vars
|
108
|
+
LANG: 'zh_CN.UTF-8'
|
109
|
+
}
|
110
|
+
};
|
111
|
+
try {
|
112
|
+
const browser = await puppeteer_extra_1.default.launch(launchOptions);
|
113
|
+
const pid = browser.process()?.pid;
|
114
|
+
logger_1.utilsLogger.debug(`Puppeteer 瀏覽器已成功啟動 (PID: ${pid})`);
|
115
|
+
// 監聽斷開連接事件
|
116
|
+
browser.on('disconnected', () => {
|
117
|
+
logger_1.utilsLogger.warn(`Puppeteer 瀏覽器 (PID: ${pid}) 已斷開連接。`);
|
118
|
+
});
|
119
|
+
// 將啟動選項附加到瀏覽器實例上,供 newPage 使用
|
120
|
+
browser.__launchOptions = options;
|
121
|
+
return browser;
|
122
|
+
}
|
123
|
+
catch (error) {
|
124
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
125
|
+
logger_1.utilsLogger.error('啟動 Puppeteer 瀏覽器失敗:', err);
|
126
|
+
// 考慮拋出更具體的錯誤類型
|
127
|
+
throw new Error(`無法啟動瀏覽器: ${err.message}`);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* 在指定的瀏覽器實例中創建一個新頁面。
|
132
|
+
* 應用通用設定,如 User-Agent 和資源攔截。
|
133
|
+
* @param browser 瀏覽器實例
|
134
|
+
* @param options 創建頁面的選項 (可覆蓋瀏覽器啟動時的選項)
|
135
|
+
* @returns Promise<Page>
|
136
|
+
*/
|
137
|
+
async function newPage(browser, options = {}) {
|
138
|
+
// 從瀏覽器實例獲取啟動選項作為預設值
|
139
|
+
const browserLaunchOptions = browser.__launchOptions || {};
|
140
|
+
// 合併選項,優先使用 newPage 傳入的選項
|
141
|
+
const pageOptions = {
|
142
|
+
// Update default User-Agent to match reference/DouyinParser.ts
|
143
|
+
userAgent: options.userAgent ?? browserLaunchOptions.userAgent ?? 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
144
|
+
blockResources: options.blockResources ?? browserLaunchOptions.blockResources ?? false, // 預設阻擋資源
|
145
|
+
timeout: options.timeout ?? browserLaunchOptions.timeout ?? 60000, // 使用瀏覽器或預設超時
|
146
|
+
};
|
147
|
+
logger_1.utilsLogger.debug(`準備獲取或創建新頁面,選項:`, pageOptions);
|
148
|
+
let page;
|
149
|
+
const pages = await browser.pages();
|
150
|
+
// 檢查是否存在一個 'about:blank' 頁面可供重複使用
|
151
|
+
if (pages.length === 1 && pages[0].url() === 'about:blank') {
|
152
|
+
logger_1.utilsLogger.debug('發現一個現有的 about:blank 頁面,將重複使用它。');
|
153
|
+
page = pages[0];
|
154
|
+
}
|
155
|
+
else {
|
156
|
+
logger_1.utilsLogger.debug('未找到可重複使用的 about:blank 頁面,創建新頁面。');
|
157
|
+
page = await browser.newPage();
|
158
|
+
}
|
159
|
+
try {
|
160
|
+
// 設定 User-Agent
|
161
|
+
await page.setUserAgent(pageOptions.userAgent);
|
162
|
+
// 設定 Accept-Language HTTP 標頭
|
163
|
+
await page.setExtraHTTPHeaders({
|
164
|
+
'Accept-Language': 'zh-CN,zh;q=0.9' // Prefer Simplified Chinese
|
165
|
+
});
|
166
|
+
logger_1.utilsLogger.debug('Accept-Language header set to zh-CN');
|
167
|
+
// 設定請求攔截 (資源阻擋)
|
168
|
+
if (pageOptions.blockResources) {
|
169
|
+
await page.setRequestInterception(true);
|
170
|
+
page.on('request', (request) => {
|
171
|
+
const resourceType = request.resourceType();
|
172
|
+
// 阻擋圖片、樣式表、字體、媒體資源
|
173
|
+
if (['image', 'stylesheet', 'font', 'media'].includes(resourceType)) { //
|
174
|
+
request.abort().catch(e => logger_1.utilsLogger.debug(`無法中止請求 ${request.url()}: ${e.message}`)); // 改用 debug 級別
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
request.continue().catch(e => logger_1.utilsLogger.debug(`無法繼續請求 ${request.url()}: ${e.message}`)); // 改用 debug 級別
|
178
|
+
}
|
179
|
+
});
|
180
|
+
}
|
181
|
+
// 設定預設超時
|
182
|
+
page.setDefaultNavigationTimeout(pageOptions.timeout * 2); // 導航超時設為兩倍
|
183
|
+
page.setDefaultTimeout(pageOptions.timeout);
|
184
|
+
logger_1.utilsLogger.debug(`頁面創建成功。`);
|
185
|
+
return page;
|
186
|
+
}
|
187
|
+
catch (error) {
|
188
|
+
logger_1.utilsLogger.error('配置新頁面失敗:', error);
|
189
|
+
// 如果頁面已創建但配置失敗,嘗試關閉它
|
190
|
+
if (!page.isClosed()) {
|
191
|
+
await page.close().catch(e => logger_1.utilsLogger.error('配置錯誤後關閉頁面失敗:', e));
|
192
|
+
}
|
193
|
+
throw error; // 重新拋出錯誤
|
194
|
+
}
|
195
|
+
}
|
196
|
+
/**
|
197
|
+
* 在指定的頁面上設置 Cookie。
|
198
|
+
* @param page Puppeteer 頁面實例
|
199
|
+
* @param cookieString Cookie 字串 (例如來自 document.cookie)
|
200
|
+
* @param domain 可選: Cookie 的域。如果未提供,則嘗試從頁面 URL 推斷。
|
201
|
+
*/
|
202
|
+
async function setCookiesOnPage(page, cookieString, domain = '.douyin.com') {
|
203
|
+
if (!cookieString) {
|
204
|
+
logger_1.utilsLogger.debug('未提供 cookie 字串,跳過 setCookiesOnPage。');
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
let targetDomain = domain;
|
208
|
+
const pageUrl = page.url();
|
209
|
+
// 如果未提供 domain,嘗試從 page URL 推斷
|
210
|
+
if (!targetDomain && pageUrl && pageUrl !== 'about:blank') {
|
211
|
+
try {
|
212
|
+
targetDomain = new URL(pageUrl).hostname;
|
213
|
+
}
|
214
|
+
catch (e) {
|
215
|
+
logger_1.utilsLogger.warn(`無法從 URL 解析 domain: ${pageUrl}`, e);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
if (!targetDomain) {
|
219
|
+
logger_1.utilsLogger.warn(`無法確定 Cookie 的域 (URL: ${pageUrl})。跳過設置 Cookie。`);
|
220
|
+
return;
|
221
|
+
}
|
222
|
+
try {
|
223
|
+
// Match reference/DouyinParser.ts: Use hardcoded domain
|
224
|
+
const cookieDomain = '.douyin.com';
|
225
|
+
logger_1.utilsLogger.debug(`解析 Cookie,將使用固定 domain: ${cookieDomain} 進行設置`);
|
226
|
+
// 解析 Cookie 字串 (Simplified like reference)
|
227
|
+
const cookiesToSet = cookieString.split(';')
|
228
|
+
.map(pair => {
|
229
|
+
const parts = pair.trim().split('=');
|
230
|
+
if (parts.length >= 2) {
|
231
|
+
const name = parts[0].trim();
|
232
|
+
const value = parts.slice(1).join('=').trim(); // Keep original value, don't decode like current code
|
233
|
+
if (!name || value === undefined)
|
234
|
+
return null;
|
235
|
+
// Set name, value, domain, and a far-future expires date for persistence
|
236
|
+
const farFuture = new Date();
|
237
|
+
farFuture.setFullYear(farFuture.getFullYear() + 1); // Expire in 1 year
|
238
|
+
return {
|
239
|
+
name,
|
240
|
+
value,
|
241
|
+
domain: cookieDomain,
|
242
|
+
expires: farFuture.getTime() / 1000, // Puppeteer expects Unix timestamp in seconds
|
243
|
+
path: '/' // Add common path
|
244
|
+
};
|
245
|
+
}
|
246
|
+
return null;
|
247
|
+
})
|
248
|
+
.filter(cookie => cookie !== null); // Simple filter
|
249
|
+
if (cookiesToSet.length > 0) {
|
250
|
+
// Use 'as any[]' like reference, although CookieParam[] should work if types match
|
251
|
+
await page.setCookie(...cookiesToSet);
|
252
|
+
logger_1.utilsLogger.debug(`為域 ${cookieDomain} 設置了 ${cookiesToSet.length} 個 Cookie`);
|
253
|
+
}
|
254
|
+
else {
|
255
|
+
logger_1.utilsLogger.warn(`從字串解析後沒有有效的 Cookie 可設置: "${cookieString}"`);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
catch (error) {
|
259
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
260
|
+
logger_1.utilsLogger.error(`設置 Cookie 失敗 (Domain: ${domain}, URL: ${pageUrl}):`, err);
|
261
|
+
// 可選擇重新拋出錯誤或進行其他處理
|
262
|
+
}
|
263
|
+
}
|
264
|
+
/**
|
265
|
+
* 安全地關閉瀏覽器實例。
|
266
|
+
* @param browser 要關閉的瀏覽器實例
|
267
|
+
*/
|
268
|
+
async function closeBrowser(browser) {
|
269
|
+
if (browser && browser.isConnected()) {
|
270
|
+
const pid = browser.process()?.pid;
|
271
|
+
logger_1.utilsLogger.debug(`正在關閉瀏覽器實例 (PID: ${pid})...`);
|
272
|
+
try {
|
273
|
+
await browser.close();
|
274
|
+
logger_1.utilsLogger.debug(`瀏覽器實例 (PID: ${pid}) 已成功關閉。`);
|
275
|
+
}
|
276
|
+
catch (error) {
|
277
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
278
|
+
logger_1.utilsLogger.error(`關閉瀏覽器實例 (PID: ${pid}) 失敗:`, err);
|
279
|
+
// 根據需要決定是否重新拋出錯誤
|
280
|
+
}
|
281
|
+
}
|
282
|
+
else {
|
283
|
+
logger_1.utilsLogger.debug('瀏覽器實例已關閉或為 null。');
|
284
|
+
}
|
285
|
+
}
|
286
|
+
/**
|
287
|
+
* 創建一個專用於登入的瀏覽器實例 (非無頭模式)。
|
288
|
+
* 返回瀏覽器、頁面和一個清理函數。
|
289
|
+
* @param options 登入瀏覽器的特定啟動選項
|
290
|
+
* @returns Promise<{ browser: Browser; page: Page; cleanup: () => Promise<void> }>
|
291
|
+
*/
|
292
|
+
async function launchLoginBrowser(options = {}) {
|
293
|
+
logger_1.utilsLogger.info('啟動專用登入瀏覽器 (非無頭模式)...');
|
294
|
+
// 強制非無頭模式,並可能使用不同的用戶數據目錄
|
295
|
+
const loginOptions = {
|
296
|
+
...options,
|
297
|
+
headless: false, // 確保非無頭
|
298
|
+
blockResources: options.blockResources ?? false, // 登入時通常需要看到所有內容
|
299
|
+
timeout: options.timeout || 120000, // 為手動登入設置更長的超時
|
300
|
+
};
|
301
|
+
let browser = null;
|
302
|
+
let page = null;
|
303
|
+
try {
|
304
|
+
// 使用通用的 launchBrowser 啟動瀏覽器
|
305
|
+
browser = await launchBrowser(loginOptions);
|
306
|
+
// 使用通用的 newPage 創建頁面,繼承部分選項
|
307
|
+
page = await newPage(browser, {
|
308
|
+
blockResources: loginOptions.blockResources,
|
309
|
+
timeout: loginOptions.timeout,
|
310
|
+
userAgent: loginOptions.userAgent, // 傳遞指定的 User Agent
|
311
|
+
});
|
312
|
+
// 特別為登入互動增加超時時間
|
313
|
+
page.setDefaultNavigationTimeout(loginOptions.timeout * 2); // 使用 ! 斷言 timeout 已有預設值
|
314
|
+
page.setDefaultTimeout(loginOptions.timeout);
|
315
|
+
// 創建清理函數
|
316
|
+
const cleanup = async () => {
|
317
|
+
logger_1.utilsLogger.debug('開始清理登入瀏覽器資源...');
|
318
|
+
// 先關閉頁面,再關閉瀏覽器
|
319
|
+
if (page && !page.isClosed()) {
|
320
|
+
try {
|
321
|
+
await page.close();
|
322
|
+
logger_1.utilsLogger.debug('登入頁面已關閉。');
|
323
|
+
}
|
324
|
+
catch (e) {
|
325
|
+
logger_1.utilsLogger.error('關閉登入頁面時出錯:', e);
|
326
|
+
}
|
327
|
+
}
|
328
|
+
// 使用通用的 closeBrowser 函數
|
329
|
+
await closeBrowser(browser);
|
330
|
+
logger_1.utilsLogger.debug('登入瀏覽器清理完成。');
|
331
|
+
};
|
332
|
+
return { browser, page, cleanup };
|
333
|
+
}
|
334
|
+
catch (error) {
|
335
|
+
logger_1.utilsLogger.error('啟動或配置登入瀏覽器失敗:', error);
|
336
|
+
// 如果部分組件已創建,嘗試清理
|
337
|
+
if (page && !page.isClosed())
|
338
|
+
await page.close().catch(e => logger_1.utilsLogger.error('登入瀏覽器清理過程中關閉頁面出錯:', e));
|
339
|
+
if (browser)
|
340
|
+
await closeBrowser(browser); // 嘗試關閉瀏覽器
|
341
|
+
throw error; // 重新拋出原始錯誤
|
342
|
+
}
|
343
|
+
}
|
344
|
+
//# sourceMappingURL=manager.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../../src/douyin/browser/manager.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/**
|
2
|
+
* 並行下載管理器
|
3
|
+
* 實現多個影片的並行下載和進度追蹤
|
4
|
+
*/
|
5
|
+
import { VideoInfo, DownloadResult as CommonDownloadResult } from '../../types';
|
6
|
+
import { VideoDownloadOptions } from './video-downloader';
|
7
|
+
/**
|
8
|
+
* 下載結果
|
9
|
+
*/
|
10
|
+
/**
|
11
|
+
* 批量下載選項
|
12
|
+
*/
|
13
|
+
export interface BatchDownloadOptions extends VideoDownloadOptions {
|
14
|
+
concurrency?: number;
|
15
|
+
onBatchProgress?: (current: number, total: number, overallProgress: number) => void;
|
16
|
+
}
|
17
|
+
/**
|
18
|
+
* 並行下載多個抖音影片
|
19
|
+
* @param videoInfoList 影片資訊列表
|
20
|
+
* @param outputPath 輸出路徑
|
21
|
+
* @param options 下載選項
|
22
|
+
* @returns Promise<DownloadResult[]> 下載結果列表
|
23
|
+
*/
|
24
|
+
export declare function downloadBatch(videoInfoList: VideoInfo[], // Use common VideoInfo
|
25
|
+
outputPath: string, options?: BatchDownloadOptions): Promise<CommonDownloadResult[]>;
|
@@ -0,0 +1,107 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/download/download-manager.ts
|
3
|
+
// Original file path: src/download/download-manager.ts
|
4
|
+
/**
|
5
|
+
* 並行下載管理器
|
6
|
+
* 實現多個影片的並行下載和進度追蹤
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.downloadBatch = downloadBatch;
|
10
|
+
const video_downloader_1 = require("./video-downloader");
|
11
|
+
const retry_1 = require("../../utils/retry"); // Updated path to shared utils
|
12
|
+
const logger_1 = require("../../utils/logger"); // Updated path to shared utils logger
|
13
|
+
/**
|
14
|
+
* 並行下載多個抖音影片
|
15
|
+
* @param videoInfoList 影片資訊列表
|
16
|
+
* @param outputPath 輸出路徑
|
17
|
+
* @param options 下載選項
|
18
|
+
* @returns Promise<DownloadResult[]> 下載結果列表
|
19
|
+
*/
|
20
|
+
async function downloadBatch(videoInfoList, // Use common VideoInfo
|
21
|
+
outputPath, options = {}) {
|
22
|
+
const { concurrency = 3, onBatchProgress, ...videoOptions } = options;
|
23
|
+
const total = videoInfoList.length;
|
24
|
+
logger_1.downloadLogger.info(`開始下載 ${total} 個影片,並行數量: ${concurrency}`);
|
25
|
+
// 用於追蹤每個影片的進度
|
26
|
+
const progressMap = new Map();
|
27
|
+
// 更新總體進度的函數
|
28
|
+
const updateOverallProgress = () => {
|
29
|
+
if (!onBatchProgress)
|
30
|
+
return;
|
31
|
+
// 計算總體進度百分比
|
32
|
+
let totalProgress = 0;
|
33
|
+
progressMap.forEach((progress) => {
|
34
|
+
totalProgress += progress;
|
35
|
+
});
|
36
|
+
const overallProgress = progressMap.size > 0
|
37
|
+
? Math.round(totalProgress / progressMap.size)
|
38
|
+
: 0;
|
39
|
+
// 回調總體進度
|
40
|
+
onBatchProgress(progressMap.size, total, overallProgress);
|
41
|
+
};
|
42
|
+
// 使用 withConcurrency 並行下載
|
43
|
+
// Use processBatch with correct types and handle its return value { results, errors }
|
44
|
+
const batchOutcome = await (0, retry_1.processBatch)(videoInfoList, async (videoInfo, index) => {
|
45
|
+
try {
|
46
|
+
// 為每個影片創建獨立的進度回調
|
47
|
+
const onProgress = (progress) => {
|
48
|
+
progressMap.set(index, progress);
|
49
|
+
updateOverallProgress();
|
50
|
+
};
|
51
|
+
// 下載影片
|
52
|
+
// Use onFileProgress from videoOptions if available
|
53
|
+
const filePath = await (0, video_downloader_1.downloadVideo)(videoInfo, outputPath, {
|
54
|
+
...videoOptions,
|
55
|
+
onFileProgress: onProgress // Pass the per-item progress callback to onFileProgress
|
56
|
+
});
|
57
|
+
// Removed the download_url logic as it's not in common VideoInfo
|
58
|
+
// 下載成功,設置進度為 100%
|
59
|
+
progressMap.set(index, 100);
|
60
|
+
updateOverallProgress();
|
61
|
+
// Return structure matching CommonDownloadResult
|
62
|
+
const result = {
|
63
|
+
videoInfo,
|
64
|
+
success: true,
|
65
|
+
filePath
|
66
|
+
};
|
67
|
+
return result;
|
68
|
+
}
|
69
|
+
catch (error) {
|
70
|
+
// 下載失敗,記錄錯誤
|
71
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
72
|
+
logger_1.downloadLogger.error(`下載影片 ${videoInfo.id} 失敗:`, errorObj);
|
73
|
+
// Return structure matching CommonDownloadResult for failure case
|
74
|
+
const result = {
|
75
|
+
videoInfo,
|
76
|
+
success: false,
|
77
|
+
error: errorObj // Use the captured error object
|
78
|
+
};
|
79
|
+
return result;
|
80
|
+
}
|
81
|
+
}, {
|
82
|
+
concurrency,
|
83
|
+
retries: 0, // 在 downloadVideo 中已經有重試機制
|
84
|
+
onProgress: (current, total) => {
|
85
|
+
if (onBatchProgress) {
|
86
|
+
// 這裡只更新完成的影片數量,不包含進度百分比
|
87
|
+
const overallProgress = progressMap.size > 0
|
88
|
+
? Math.round(Array.from(progressMap.values()).reduce((sum, p) => sum + p, 0) / progressMap.size)
|
89
|
+
: 0;
|
90
|
+
onBatchProgress(current, total, overallProgress);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}); // batchOutcome is { results: CommonDownloadResult[], errors: Error[] }
|
94
|
+
// 統計成功和失敗數量
|
95
|
+
// Extract results and errors
|
96
|
+
const successfulDownloads = batchOutcome.results;
|
97
|
+
const downloadErrors = batchOutcome.errors;
|
98
|
+
const successCount = successfulDownloads.length;
|
99
|
+
const failCount = total - successCount; // Or use downloadErrors.length
|
100
|
+
logger_1.downloadLogger.info(`下載完成,成功: ${successCount}/${total},失敗: ${failCount}/${total}`);
|
101
|
+
if (downloadErrors.length > 0) {
|
102
|
+
logger_1.downloadLogger.warn(`下載過程中發生 ${downloadErrors.length} 個錯誤`);
|
103
|
+
}
|
104
|
+
// Return the array of successful download results
|
105
|
+
return successfulDownloads;
|
106
|
+
}
|
107
|
+
//# sourceMappingURL=download-manager.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"download-manager.js","sourceRoot":"","sources":["../../../../src/douyin/download/download-manager.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
/**
|
2
|
+
* 錯誤處理和重試機制
|
3
|
+
* 提供下載錯誤處理和智能重試邏輯
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* 下載錯誤類型
|
7
|
+
*/
|
8
|
+
export declare class DownloadError extends Error {
|
9
|
+
readonly code: string;
|
10
|
+
readonly retryable: boolean;
|
11
|
+
readonly url: string;
|
12
|
+
constructor(message: string, url: string, code?: string, retryable?: boolean);
|
13
|
+
/**
|
14
|
+
* 判斷錯誤是否可重試
|
15
|
+
*/
|
16
|
+
isRetryable(): boolean;
|
17
|
+
}
|
18
|
+
/**
|
19
|
+
* 重試選項
|
20
|
+
*/
|
21
|
+
export interface RetryOptions {
|
22
|
+
retries: number;
|
23
|
+
minDelay?: number;
|
24
|
+
maxDelay?: number;
|
25
|
+
factor?: number;
|
26
|
+
onRetry?: (error: Error, attempt: number) => void;
|
27
|
+
onProgress?: (progress: number) => void;
|
28
|
+
}
|
29
|
+
/**
|
30
|
+
* 帶重試機制的下載函數
|
31
|
+
* @param downloadFn 下載函數
|
32
|
+
* @param url 下載 URL
|
33
|
+
* @param options 重試選項
|
34
|
+
* @returns Promise<T> 下載結果
|
35
|
+
*/
|
36
|
+
export declare function downloadWithRetry<T>(downloadFn: (onProgress?: (progress: number) => void) => Promise<T>, url: string, options: RetryOptions): Promise<T>;
|
37
|
+
/**
|
38
|
+
* 判斷錯誤是否可重試
|
39
|
+
* @param error 錯誤對象
|
40
|
+
* @returns 是否可重試
|
41
|
+
*/
|
42
|
+
export declare function isRetryableError(error: Error): boolean;
|
43
|
+
/**
|
44
|
+
* 處理下載錯誤並返回友好的錯誤消息
|
45
|
+
* @param error 原始錯誤
|
46
|
+
* @param url 下載 URL
|
47
|
+
* @returns 友好的錯誤消息
|
48
|
+
*/
|
49
|
+
export declare function getDownloadErrorMessage(error: unknown, url: string): string;
|