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,160 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/download/error-handler.ts
|
3
|
+
// Original file path: src/download/error-handler.ts
|
4
|
+
/**
|
5
|
+
* 錯誤處理和重試機制
|
6
|
+
* 提供下載錯誤處理和智能重試邏輯
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.DownloadError = void 0;
|
10
|
+
exports.downloadWithRetry = downloadWithRetry;
|
11
|
+
exports.isRetryableError = isRetryableError;
|
12
|
+
exports.getDownloadErrorMessage = getDownloadErrorMessage;
|
13
|
+
const logger_1 = require("../../utils/logger"); // Updated path to shared utils logger
|
14
|
+
// No need to import withRetry here, it seems to be used elsewhere (e.g., video-downloader)
|
15
|
+
/**
|
16
|
+
* 下載錯誤類型
|
17
|
+
*/
|
18
|
+
class DownloadError extends Error {
|
19
|
+
code;
|
20
|
+
retryable;
|
21
|
+
url;
|
22
|
+
constructor(message, url, code = 'DOWNLOAD_ERROR', retryable = true) {
|
23
|
+
super(message);
|
24
|
+
this.name = 'DownloadError';
|
25
|
+
this.code = code;
|
26
|
+
this.retryable = retryable;
|
27
|
+
this.url = url;
|
28
|
+
}
|
29
|
+
/**
|
30
|
+
* 判斷錯誤是否可重試
|
31
|
+
*/
|
32
|
+
isRetryable() {
|
33
|
+
return this.retryable;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
exports.DownloadError = DownloadError;
|
37
|
+
/**
|
38
|
+
* 計算重試延遲時間
|
39
|
+
* 使用指數退避策略
|
40
|
+
* @param attempt 當前嘗試次數(從 1 開始)
|
41
|
+
* @param options 重試選項
|
42
|
+
* @returns 延遲時間(毫秒)
|
43
|
+
*/
|
44
|
+
function calculateDelay(attempt, options) {
|
45
|
+
const { minDelay = 1000, maxDelay = 30000, factor = 2 } = options;
|
46
|
+
// 指數退避: minDelay * (factor ^ (attempt - 1))
|
47
|
+
let delay = minDelay * Math.pow(factor, attempt - 1);
|
48
|
+
// 添加一些隨機性以避免 "雷暴" 效應
|
49
|
+
delay = delay * (0.8 + Math.random() * 0.4);
|
50
|
+
// 限制在最大延遲時間內
|
51
|
+
return Math.min(delay, maxDelay);
|
52
|
+
}
|
53
|
+
/**
|
54
|
+
* 帶重試機制的下載函數
|
55
|
+
* @param downloadFn 下載函數
|
56
|
+
* @param url 下載 URL
|
57
|
+
* @param options 重試選項
|
58
|
+
* @returns Promise<T> 下載結果
|
59
|
+
*/
|
60
|
+
async function downloadWithRetry(downloadFn, url, options) {
|
61
|
+
const { retries, onRetry, onProgress } = options;
|
62
|
+
let attempt = 0;
|
63
|
+
let lastError;
|
64
|
+
while (attempt <= retries) {
|
65
|
+
attempt++;
|
66
|
+
try {
|
67
|
+
// 執行下載函數
|
68
|
+
return await downloadFn(onProgress);
|
69
|
+
}
|
70
|
+
catch (error) {
|
71
|
+
// 轉換為 Error 對象
|
72
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
73
|
+
// 檢查錯誤是否可重試
|
74
|
+
const isRetryable = error instanceof DownloadError
|
75
|
+
? error.isRetryable()
|
76
|
+
: isRetryableError(lastError);
|
77
|
+
// 如果已達到最大重試次數或錯誤不可重試,拋出錯誤
|
78
|
+
if (attempt > retries || !isRetryable) {
|
79
|
+
logger_1.downloadLogger.error(`下載 ${url} 失敗,不再重試`, lastError);
|
80
|
+
throw lastError;
|
81
|
+
}
|
82
|
+
// 調用重試回調
|
83
|
+
if (onRetry) {
|
84
|
+
onRetry(lastError, attempt);
|
85
|
+
}
|
86
|
+
// 記錄日誌
|
87
|
+
logger_1.downloadLogger.warn(`下載 ${url} 失敗,準備重試 #${attempt}/${retries}:`, lastError.message);
|
88
|
+
// 計算延遲時間
|
89
|
+
const delay = calculateDelay(attempt, options);
|
90
|
+
// 等待後重試
|
91
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
92
|
+
}
|
93
|
+
}
|
94
|
+
// 理論上不應該執行到這裡
|
95
|
+
throw lastError;
|
96
|
+
}
|
97
|
+
/**
|
98
|
+
* 判斷錯誤是否可重試
|
99
|
+
* @param error 錯誤對象
|
100
|
+
* @returns 是否可重試
|
101
|
+
*/
|
102
|
+
function isRetryableError(error) {
|
103
|
+
const message = error.message.toLowerCase();
|
104
|
+
const name = error.name.toLowerCase();
|
105
|
+
// 網絡錯誤通常可重試
|
106
|
+
if (name.includes('network') ||
|
107
|
+
message.includes('network') ||
|
108
|
+
message.includes('connection') ||
|
109
|
+
message.includes('timeout') ||
|
110
|
+
message.includes('timed out') ||
|
111
|
+
message.includes('429') || // Too Many Requests
|
112
|
+
message.includes('503') || // Service Unavailable
|
113
|
+
message.includes('504') // Gateway Timeout
|
114
|
+
) {
|
115
|
+
return true;
|
116
|
+
}
|
117
|
+
// 資源錯誤通常不可重試
|
118
|
+
if (message.includes('404') || // Not Found
|
119
|
+
message.includes('403') || // Forbidden
|
120
|
+
message.includes('401') || // Unauthorized
|
121
|
+
message.includes('400') || // Bad Request
|
122
|
+
message.includes('not found') ||
|
123
|
+
message.includes('forbidden') ||
|
124
|
+
message.includes('unauthorized') ||
|
125
|
+
message.includes('bad request') ||
|
126
|
+
message.includes('permission denied')) {
|
127
|
+
return false;
|
128
|
+
}
|
129
|
+
// 默認可重試
|
130
|
+
return true;
|
131
|
+
}
|
132
|
+
/**
|
133
|
+
* 處理下載錯誤並返回友好的錯誤消息
|
134
|
+
* @param error 原始錯誤
|
135
|
+
* @param url 下載 URL
|
136
|
+
* @returns 友好的錯誤消息
|
137
|
+
*/
|
138
|
+
function getDownloadErrorMessage(error, url) {
|
139
|
+
if (error instanceof DownloadError) {
|
140
|
+
return error.message;
|
141
|
+
}
|
142
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
143
|
+
const message = err.message.toLowerCase();
|
144
|
+
// 常見錯誤類型的友好消息
|
145
|
+
if (message.includes('timeout') || message.includes('timed out')) {
|
146
|
+
return `下載超時,請檢查網絡連接或稍後再試`;
|
147
|
+
}
|
148
|
+
if (message.includes('404') || message.includes('not found')) {
|
149
|
+
return `影片資源不存在或已被移除`;
|
150
|
+
}
|
151
|
+
if (message.includes('403') || message.includes('forbidden')) {
|
152
|
+
return `無權訪問此影片資源,可能需要登入或權限`;
|
153
|
+
}
|
154
|
+
if (message.includes('429') || message.includes('too many requests')) {
|
155
|
+
return `下載請求過於頻繁,請稍後再試`;
|
156
|
+
}
|
157
|
+
// 其他錯誤使用原始消息
|
158
|
+
return `下載影片時發生錯誤: ${err.message}`;
|
159
|
+
}
|
160
|
+
//# sourceMappingURL=error-handler.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../../../src/douyin/download/error-handler.ts"],"names":[],"mappings":";AAAA,4CAA4C;AAC5C,oDAAoD;AACpD;;;GAGG;;;AAyEH,8CAmDC;AAOD,4CAmCC;AAQD,0DA2BC;AAvMD,+CAA8D,CAAC,sCAAsC;AACrG,2FAA2F;AAE3F;;GAEG;AACH,MAAa,aAAc,SAAQ,KAAK;IACtB,IAAI,CAAS;IACb,SAAS,CAAU;IACnB,GAAG,CAAS;IAE5B,YACE,OAAe,EACf,GAAW,EACX,OAAe,gBAAgB,EAC/B,YAAqB,IAAI;QAEzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED;;OAEG;IACI,WAAW;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF;AAxBD,sCAwBC;AAcD;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,OAAe,EAAE,OAAqB;IAC5D,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,QAAQ,GAAG,KAAK,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAElE,4CAA4C;IAC5C,IAAI,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IAErD,qBAAqB;IACrB,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAE5C,aAAa;IACb,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,iBAAiB,CACrC,UAAmE,EACnE,GAAW,EACX,OAAqB;IAErB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAEjD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAgB,CAAC;IAErB,OAAO,OAAO,IAAI,OAAO,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;QAEV,IAAI,CAAC;YACH,SAAS;YACT,OAAO,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAe;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAEtE,YAAY;YACZ,MAAM,WAAW,GACf,KAAK,YAAY,aAAa;gBAC5B,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE;gBACrB,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAElC,0BAA0B;YAC1B,IAAI,OAAO,GAAG,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtC,uBAAM,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC;gBAC7C,MAAM,SAAS,CAAC;YAClB,CAAC;YAED,SAAS;YACT,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC9B,CAAC;YAED,OAAO;YACP,uBAAM,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,OAAO,IAAI,OAAO,GAAG,EACrD,SAAS,CAAC,OAAO,CAAC,CAAC;YAErB,SAAS;YACT,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAE/C,QAAQ;YACR,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,cAAc;IACd,MAAM,SAAU,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,KAAY;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAEtC,YAAY;IACZ,IACE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,oBAAoB;QAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,sBAAsB;QACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAI,kBAAkB;MAC7C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa;IACb,IACE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,YAAY;QACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,YAAY;QACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,eAAe;QAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,cAAc;QACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EACrC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ;IACR,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAgB,uBAAuB,CAAC,KAAc,EAAE,GAAW;IACjE,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAE1C,cAAc;IACd,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACjE,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7D,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACrE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,aAAa;IACb,OAAO,cAAc,GAAG,CAAC,OAAO,EAAE,CAAC;AACrC,CAAC"}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/**
|
2
|
+
* 影片下載模組
|
3
|
+
* 提供下載抖音影片的功能
|
4
|
+
*/
|
5
|
+
import { VideoInfo, DownloadOptions, DownloadResult as CommonDownloadResult } from '../../types';
|
6
|
+
import { Page } from 'puppeteer';
|
7
|
+
export * from './video-downloader';
|
8
|
+
export * from './download-manager';
|
9
|
+
export * from './path-formatter';
|
10
|
+
export * from './error-handler';
|
11
|
+
/**
|
12
|
+
* 下載多個抖音影片
|
13
|
+
* @param videoInfos 影片資訊陣列
|
14
|
+
* @param outputPath 輸出路徑
|
15
|
+
* @param options 下載選項
|
16
|
+
* @returns Promise<DownloadResult[]> 下載結果陣列
|
17
|
+
*/
|
18
|
+
export declare function downloadVideos(videoInfos: VideoInfo[], // Use common VideoInfo
|
19
|
+
outputPath: string, options?: DownloadOptions): Promise<CommonDownloadResult[]>;
|
20
|
+
/**
|
21
|
+
* 從 URL 陣列下載抖音影片
|
22
|
+
* @param videoUrls 影片 URL 陣列
|
23
|
+
* @param outputPath 輸出路徑
|
24
|
+
* @param cookie 有效的抖音 Cookie
|
25
|
+
* @param options 下載選項
|
26
|
+
* @returns Promise<string[]> 下載完成的檔案路徑陣列
|
27
|
+
*/
|
28
|
+
export declare function downloadVideosFromUrls(videoUrls: string[], outputPath: string, page: Page, // Changed: Accept Page object
|
29
|
+
options?: DownloadOptions): Promise<string[]>;
|
30
|
+
/**
|
31
|
+
* 從文本中解析抖音連結並下載影片
|
32
|
+
* @param text 包含抖音連結的文本
|
33
|
+
* @param outputPath 輸出路徑
|
34
|
+
* @param cookie 有效的抖音 Cookie
|
35
|
+
* @param options 下載選項
|
36
|
+
* @returns Promise<string[]> 下載完成的檔案路徑陣列
|
37
|
+
*/
|
38
|
+
export declare function downloadVideosFromText(text: string, outputPath: string, page: Page, // Changed: Accept Page object
|
39
|
+
options?: DownloadOptions): Promise<string[]>;
|
@@ -0,0 +1,156 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/download/index.ts
|
3
|
+
// Original file path: src/download/index.ts
|
4
|
+
/**
|
5
|
+
* 影片下載模組
|
6
|
+
* 提供下載抖音影片的功能
|
7
|
+
*/
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
9
|
+
if (k2 === undefined) k2 = k;
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
13
|
+
}
|
14
|
+
Object.defineProperty(o, k2, desc);
|
15
|
+
}) : (function(o, m, k, k2) {
|
16
|
+
if (k2 === undefined) k2 = k;
|
17
|
+
o[k2] = m[k];
|
18
|
+
}));
|
19
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
20
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
21
|
+
};
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
23
|
+
exports.downloadVideos = downloadVideos;
|
24
|
+
exports.downloadVideosFromUrls = downloadVideosFromUrls;
|
25
|
+
exports.downloadVideosFromText = downloadVideosFromText;
|
26
|
+
const logger_1 = require("../../utils/logger"); // Updated path to shared utils logger
|
27
|
+
const info_1 = require("../info"); // Keep relative path within douyin
|
28
|
+
const parser_1 = require("../parser"); // Keep relative path within douyin
|
29
|
+
const download_manager_1 = require("./download-manager"); // Remove DownloadResult import
|
30
|
+
// 重新導出子模組
|
31
|
+
__exportStar(require("./video-downloader"), exports);
|
32
|
+
__exportStar(require("./download-manager"), exports);
|
33
|
+
__exportStar(require("./path-formatter"), exports);
|
34
|
+
__exportStar(require("./error-handler"), exports);
|
35
|
+
/**
|
36
|
+
* 下載多個抖音影片
|
37
|
+
* @param videoInfos 影片資訊陣列
|
38
|
+
* @param outputPath 輸出路徑
|
39
|
+
* @param options 下載選項
|
40
|
+
* @returns Promise<DownloadResult[]> 下載結果陣列
|
41
|
+
*/
|
42
|
+
async function downloadVideos(videoInfos, // Use common VideoInfo
|
43
|
+
outputPath, options = {}) {
|
44
|
+
// Destructure all potential options from the input DownloadOptions
|
45
|
+
const { concurrency = 3, retries = 3, onProgress, timeout,
|
46
|
+
// headers are not part of DownloadOptions in types.ts, but are in VideoDownloadOptions
|
47
|
+
// checkExisting is not part of DownloadOptions in types.ts, but are in VideoDownloadOptions
|
48
|
+
overwrite, useSubfolders, filenameTemplate } = options;
|
49
|
+
// 目錄確保邏輯已移至 CLI 或 downloadBatch/downloadVideo
|
50
|
+
// await ensureDirectoryExists(path.isAbsolute(outputPath)
|
51
|
+
// ? outputPath
|
52
|
+
// : path.resolve(process.cwd(), outputPath));
|
53
|
+
logger_1.downloadLogger.info(`開始下載 ${videoInfos.length} 個影片`);
|
54
|
+
// 使用下載批次處理器
|
55
|
+
// Explicitly construct the options object for downloadBatch using an intersection type
|
56
|
+
const batchOptions = {
|
57
|
+
concurrency,
|
58
|
+
retries, // Pass retries (should be valid via VideoDownloadOptions)
|
59
|
+
onBatchProgress: onProgress, // Map the batch progress callback
|
60
|
+
// Pass other relevant options inherited from VideoDownloadOptions
|
61
|
+
timeout: timeout,
|
62
|
+
// headers: headers, // headers not available in input options
|
63
|
+
// checkExisting: checkExisting, // checkExisting not available in input options
|
64
|
+
overwrite: overwrite,
|
65
|
+
useSubfolders: useSubfolders,
|
66
|
+
filenameTemplate: filenameTemplate
|
67
|
+
};
|
68
|
+
const results = await (0, download_manager_1.downloadBatch)(videoInfos, outputPath, batchOptions);
|
69
|
+
// 統計結果
|
70
|
+
const successCount = results.filter(r => r.success).length;
|
71
|
+
const failCount = results.filter(r => !r.success).length;
|
72
|
+
logger_1.downloadLogger.info(`下載完成,成功: ${successCount}/${videoInfos.length},失敗: ${failCount}/${videoInfos.length}`);
|
73
|
+
return results;
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* 從 URL 陣列下載抖音影片
|
77
|
+
* @param videoUrls 影片 URL 陣列
|
78
|
+
* @param outputPath 輸出路徑
|
79
|
+
* @param cookie 有效的抖音 Cookie
|
80
|
+
* @param options 下載選項
|
81
|
+
* @returns Promise<string[]> 下載完成的檔案路徑陣列
|
82
|
+
*/
|
83
|
+
async function downloadVideosFromUrls(videoUrls, outputPath, page, // Changed: Accept Page object
|
84
|
+
// cookie: string, // Removed
|
85
|
+
options = {}) {
|
86
|
+
// 獲取影片資訊
|
87
|
+
logger_1.downloadLogger.info(`[downloadVideosFromUrls] 開始獲取 ${videoUrls.length} 個影片的資訊`);
|
88
|
+
// Pass page instead of cookie
|
89
|
+
const videoInfos = await (0, info_1.fetchVideosInfoUrls)(videoUrls, page, {
|
90
|
+
concurrency: options.concurrency,
|
91
|
+
onProgress: options.onProgress
|
92
|
+
? (current, total, overallProgress) => options.onProgress(current, total, overallProgress !== undefined ? overallProgress * 0.2 : undefined) // Adjust progress, handle undefined
|
93
|
+
: undefined
|
94
|
+
});
|
95
|
+
if (videoInfos.length === 0) {
|
96
|
+
logger_1.downloadLogger.warn('[downloadVideosFromUrls] 未找到有效的影片資訊,無法下載');
|
97
|
+
return [];
|
98
|
+
}
|
99
|
+
logger_1.downloadLogger.info(`[downloadVideosFromUrls] 成功獲取 ${videoInfos.length} 個影片的資訊,開始下載`);
|
100
|
+
// 下載影片
|
101
|
+
const results = await downloadVideos(videoInfos, outputPath, {
|
102
|
+
...options,
|
103
|
+
onProgress: options.onProgress
|
104
|
+
? (current, total, overallProgress) => {
|
105
|
+
// 調整進度:資訊獲取 0-20%,下載 20-100%
|
106
|
+
const adjustedProgress = overallProgress !== undefined ? 20 + (overallProgress * 0.8) : undefined; // Adjust progress, handle undefined
|
107
|
+
options.onProgress(current, total, adjustedProgress);
|
108
|
+
}
|
109
|
+
: undefined
|
110
|
+
});
|
111
|
+
// 過濾出成功的結果,返回檔案路徑
|
112
|
+
return results
|
113
|
+
.filter(result => result.success && result.filePath)
|
114
|
+
.map(result => result.filePath);
|
115
|
+
}
|
116
|
+
/**
|
117
|
+
* 從文本中解析抖音連結並下載影片
|
118
|
+
* @param text 包含抖音連結的文本
|
119
|
+
* @param outputPath 輸出路徑
|
120
|
+
* @param cookie 有效的抖音 Cookie
|
121
|
+
* @param options 下載選項
|
122
|
+
* @returns Promise<string[]> 下載完成的檔案路徑陣列
|
123
|
+
*/
|
124
|
+
async function downloadVideosFromText(text, outputPath, page, // Changed: Accept Page object
|
125
|
+
// cookie: string, // Removed
|
126
|
+
options = {}) {
|
127
|
+
// 解析文本中的抖音連結
|
128
|
+
logger_1.downloadLogger.info('[downloadVideosFromText] 解析文本中的抖音連結');
|
129
|
+
const parseResults = await (0, parser_1.parseDouyinLinks)(text, {
|
130
|
+
concurrency: options.concurrency,
|
131
|
+
retries: options.retries,
|
132
|
+
onProgress: options.onProgress
|
133
|
+
? (current, total, overallProgress) => options.onProgress(current, total, overallProgress !== undefined ? overallProgress * 0.1 : undefined) // Adjust progress, handle undefined
|
134
|
+
: undefined
|
135
|
+
});
|
136
|
+
if (parseResults.length === 0) {
|
137
|
+
logger_1.downloadLogger.warn('[downloadVideosFromText] 未找到有效的抖音影片連結');
|
138
|
+
return [];
|
139
|
+
}
|
140
|
+
// 獲取標準 URL 陣列
|
141
|
+
const videoUrls = parseResults.map((result) => result.standardUrl);
|
142
|
+
logger_1.downloadLogger.info(`[downloadVideosFromText] 找到 ${videoUrls.length} 個有效連結,開始獲取影片資訊`);
|
143
|
+
// 下載影片
|
144
|
+
// Pass page instead of cookie
|
145
|
+
return downloadVideosFromUrls(videoUrls, outputPath, page, {
|
146
|
+
...options,
|
147
|
+
onProgress: options.onProgress
|
148
|
+
? (current, total, overallProgress) => {
|
149
|
+
// 調整進度:解析連結 0-10%,下載 10-100%
|
150
|
+
const adjustedProgress = overallProgress !== undefined ? 10 + (overallProgress * 0.9) : undefined; // Adjust progress, handle undefined
|
151
|
+
options.onProgress(current, total, adjustedProgress);
|
152
|
+
}
|
153
|
+
: undefined
|
154
|
+
});
|
155
|
+
}
|
156
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/douyin/download/index.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
/**
|
2
|
+
* 檔名和路徑管理
|
3
|
+
* 提供格式化檔名和路徑的功能
|
4
|
+
*/
|
5
|
+
import { VideoInfo } from '../../types';
|
6
|
+
/**
|
7
|
+
* 格式化輸出路徑選項
|
8
|
+
*/
|
9
|
+
export interface PathFormatterOptions {
|
10
|
+
useSubfolders?: boolean;
|
11
|
+
filenameTemplate?: string;
|
12
|
+
maxFilenameLength?: number;
|
13
|
+
}
|
14
|
+
/**
|
15
|
+
* 獲取安全的檔案名(替換非法字符)
|
16
|
+
* @param filename 原始檔案名
|
17
|
+
* @param maxLength 最大長度限制
|
18
|
+
* @returns 安全的檔案名
|
19
|
+
*/
|
20
|
+
/**
|
21
|
+
* 格式化日期字符串
|
22
|
+
* @param dateStr 日期字符串 (ISO 格式或 YYYY-MM-DD)
|
23
|
+
* @returns 格式化後的日期 (YYYYMMDD)
|
24
|
+
*/
|
25
|
+
export declare function formatDate(dateStr: string): string;
|
26
|
+
/**
|
27
|
+
* 應用檔名模板
|
28
|
+
* @param template 模板字符串
|
29
|
+
* @param videoInfo 影片資訊
|
30
|
+
* @returns 應用模板後的字符串
|
31
|
+
*/
|
32
|
+
export declare function applyFilenameTemplate(template: string, videoInfo: VideoInfo): string;
|
33
|
+
/**
|
34
|
+
* 格式化輸出路徑
|
35
|
+
* 根據影片資訊和選項,確定檔案的完整路徑
|
36
|
+
* @param videoInfo 影片資訊
|
37
|
+
* @param baseOutputPath 基本輸出路徑
|
38
|
+
* @param options 格式化選項
|
39
|
+
* @returns 格式化後的完整檔案路徑
|
40
|
+
*/
|
41
|
+
export declare function formatOutputPath(videoInfo: VideoInfo, // Use common VideoInfo
|
42
|
+
baseOutputPath: string, options?: PathFormatterOptions): string;
|
@@ -0,0 +1,107 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/download/path-formatter.ts
|
3
|
+
// Original file path: src/download/path-formatter.ts
|
4
|
+
/**
|
5
|
+
* 檔名和路徑管理
|
6
|
+
* 提供格式化檔名和路徑的功能
|
7
|
+
*/
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.formatDate = formatDate;
|
13
|
+
exports.applyFilenameTemplate = applyFilenameTemplate;
|
14
|
+
exports.formatOutputPath = formatOutputPath;
|
15
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
16
|
+
const path_1 = __importDefault(require("path"));
|
17
|
+
const file_1 = require("../../utils/file"); // Import from shared utils
|
18
|
+
/**
|
19
|
+
* 獲取安全的檔案名(替換非法字符)
|
20
|
+
* @param filename 原始檔案名
|
21
|
+
* @param maxLength 最大長度限制
|
22
|
+
* @returns 安全的檔案名
|
23
|
+
*/
|
24
|
+
// Moved getSafeFilename to src/utils/file.ts
|
25
|
+
/**
|
26
|
+
* 格式化日期字符串
|
27
|
+
* @param dateStr 日期字符串 (ISO 格式或 YYYY-MM-DD)
|
28
|
+
* @returns 格式化後的日期 (YYYYMMDD)
|
29
|
+
*/
|
30
|
+
function formatDate(dateStr) {
|
31
|
+
try {
|
32
|
+
const date = new Date(dateStr);
|
33
|
+
if (isNaN(date.getTime())) {
|
34
|
+
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
35
|
+
}
|
36
|
+
return date.toISOString().slice(0, 10).replace(/-/g, '');
|
37
|
+
}
|
38
|
+
catch (error) {
|
39
|
+
// 如果解析失敗,使用當前日期
|
40
|
+
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
41
|
+
}
|
42
|
+
}
|
43
|
+
/**
|
44
|
+
* 應用檔名模板
|
45
|
+
* @param template 模板字符串
|
46
|
+
* @param videoInfo 影片資訊
|
47
|
+
* @returns 應用模板後的字符串
|
48
|
+
*/
|
49
|
+
function applyFilenameTemplate(template, videoInfo // Use common VideoInfo
|
50
|
+
) {
|
51
|
+
// 將可用的變數替換為實際值
|
52
|
+
return template
|
53
|
+
.replace(/{id}/g, videoInfo.id || 'unknown')
|
54
|
+
.replace(/{title}/g, videoInfo.title || 'untitled')
|
55
|
+
.replace(/{date}/g, formatDate(videoInfo.releaseDate || ''))
|
56
|
+
.replace(/{user}/g, videoInfo.userName || 'unknown_user');
|
57
|
+
}
|
58
|
+
/**
|
59
|
+
* 格式化輸出路徑
|
60
|
+
* 根據影片資訊和選項,確定檔案的完整路徑
|
61
|
+
* @param videoInfo 影片資訊
|
62
|
+
* @param baseOutputPath 基本輸出路徑
|
63
|
+
* @param options 格式化選項
|
64
|
+
* @returns 格式化後的完整檔案路徑
|
65
|
+
*/
|
66
|
+
function formatOutputPath(videoInfo, // Use common VideoInfo
|
67
|
+
baseOutputPath, options = {}) {
|
68
|
+
const { useSubfolders = true, filenameTemplate = "{date}-{title}", maxFilenameLength = 200 } = options;
|
69
|
+
// 檢查基本路徑是否是目錄
|
70
|
+
let isDirectory = false;
|
71
|
+
try {
|
72
|
+
isDirectory = fs_extra_1.default.statSync(baseOutputPath).isDirectory();
|
73
|
+
}
|
74
|
+
catch (error) {
|
75
|
+
// 檢查路徑是否以目錄分隔符結尾
|
76
|
+
isDirectory = baseOutputPath.endsWith(path_1.default.sep) || baseOutputPath.endsWith('/');
|
77
|
+
}
|
78
|
+
// 如果是目錄,根據選項決定使用子資料夾
|
79
|
+
if (isDirectory) {
|
80
|
+
let targetDir = baseOutputPath;
|
81
|
+
// 如果使用子資料夾,則添加使用者名稱資料夾
|
82
|
+
if (useSubfolders && videoInfo.userName) {
|
83
|
+
const userDirName = (0, file_1.getSafeFilename)(videoInfo.userName || 'unknown_user'); // Remove second argument
|
84
|
+
targetDir = path_1.default.join(baseOutputPath, userDirName);
|
85
|
+
}
|
86
|
+
// 應用檔名模板並限制長度
|
87
|
+
// Apply template first, then sanitize the result using the single-argument getSafeFilename
|
88
|
+
const templateAppliedName = applyFilenameTemplate(filenameTemplate, videoInfo);
|
89
|
+
const filename = (0, file_1.getSafeFilename)(templateAppliedName); // Remove second argument
|
90
|
+
// 確保檔名有 .mp4 擴展名
|
91
|
+
const finalName = filename.toLowerCase().endsWith('.mp4')
|
92
|
+
? filename
|
93
|
+
: `${filename}.mp4`;
|
94
|
+
return path_1.default.join(targetDir, finalName);
|
95
|
+
}
|
96
|
+
else {
|
97
|
+
// 如果不是目錄,則直接使用指定的檔案路徑
|
98
|
+
let finalPath = baseOutputPath;
|
99
|
+
// 確保檔案擴展名為 .mp4
|
100
|
+
if (!finalPath.toLowerCase().endsWith('.mp4')) {
|
101
|
+
finalPath += '.mp4';
|
102
|
+
}
|
103
|
+
return finalPath;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
// Moved ensureDirectoryExists to src/utils/file.ts
|
107
|
+
//# sourceMappingURL=path-formatter.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"path-formatter.js","sourceRoot":"","sources":["../../../../src/douyin/download/path-formatter.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/**
|
2
|
+
* 影片下載核心功能
|
3
|
+
* 實現單個影片的下載邏輯
|
4
|
+
*/
|
5
|
+
import { VideoInfo } from '../../types';
|
6
|
+
/**
|
7
|
+
* 下載選項
|
8
|
+
*/
|
9
|
+
export interface VideoDownloadOptions {
|
10
|
+
retries?: number;
|
11
|
+
timeout?: number;
|
12
|
+
headers?: Record<string, string>;
|
13
|
+
onProgress?: (progress: number) => void;
|
14
|
+
onFileProgress?: (progress: number) => void;
|
15
|
+
checkExisting?: boolean;
|
16
|
+
overwrite?: boolean;
|
17
|
+
useSubfolders?: boolean;
|
18
|
+
filenameTemplate?: string;
|
19
|
+
}
|
20
|
+
/**
|
21
|
+
* 下載單個抖音影片
|
22
|
+
* @param videoInfo 影片資訊
|
23
|
+
* @param destPath 目標路徑(檔案或目錄)
|
24
|
+
* @param options 下載選項
|
25
|
+
* @returns Promise<string> 下載完成的檔案路徑
|
26
|
+
*/
|
27
|
+
export declare function downloadVideo(videoInfo: VideoInfo, // Use common VideoInfo
|
28
|
+
destPath: string, options?: VideoDownloadOptions, downloadUrls?: string[]): Promise<string>;
|
29
|
+
/**
|
30
|
+
* 獲取影片的下載大小
|
31
|
+
* @param url 影片 URL
|
32
|
+
* @param headers 請求頭
|
33
|
+
* @returns Promise<number> 檔案大小(位元組)
|
34
|
+
*/
|
35
|
+
export declare function getVideoSize(url: string, headers?: Record<string, string>): Promise<number>;
|