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,121 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/parser/link-patterns.ts
|
3
|
+
// Original file path: src/parser/link-patterns.ts
|
4
|
+
/**
|
5
|
+
* 抖音連結模式定義
|
6
|
+
* 定義各種抖音連結的正則表達式和處理方式
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.DOUYIN_LINK_PATTERNS = void 0;
|
10
|
+
exports.createCombinedPattern = createCombinedPattern;
|
11
|
+
exports.getLinkType = getLinkType;
|
12
|
+
exports.extractVideoId = extractVideoId;
|
13
|
+
const logger_1 = require("../../utils/logger"); // Corrected path
|
14
|
+
/**
|
15
|
+
* 抖音連結模式陣列
|
16
|
+
* 包含各種可能的抖音連結格式
|
17
|
+
*/
|
18
|
+
exports.DOUYIN_LINK_PATTERNS = [
|
19
|
+
{
|
20
|
+
name: 'standard',
|
21
|
+
// 標準網頁連結,例如: https://www.douyin.com/video/7123456789
|
22
|
+
pattern: /https?:\/\/(?:www\.)?douyin\.com\/video\/(\d+)/,
|
23
|
+
needsRedirect: false
|
24
|
+
},
|
25
|
+
{
|
26
|
+
name: 'short',
|
27
|
+
// 短連結,例如: https://v.douyin.com/abc123/ 或 https://v.douyin.com/c-mY7xDr-qA/
|
28
|
+
pattern: /https?:\/\/v\.douyin\.com\/([a-zA-Z0-9-]+)\/?/,
|
29
|
+
needsRedirect: true
|
30
|
+
},
|
31
|
+
{
|
32
|
+
name: 'mobile',
|
33
|
+
// 移動端分享連結,例如: https://www.douyin.com/share/video/123456789
|
34
|
+
pattern: /https?:\/\/(?:www\.)?douyin\.com\/share\/video\/(\d+)/,
|
35
|
+
needsRedirect: true
|
36
|
+
},
|
37
|
+
{
|
38
|
+
name: 'note',
|
39
|
+
// 筆記類型連結,例如: https://www.douyin.com/note/123456789
|
40
|
+
pattern: /https?:\/\/(?:www\.)?douyin\.com\/note\/(\d+)/,
|
41
|
+
needsRedirect: true
|
42
|
+
},
|
43
|
+
{
|
44
|
+
name: 'discover',
|
45
|
+
// 發現頁連結,例如: https://www.douyin.com/discover?modal_id=7123456789
|
46
|
+
pattern: /https?:\/\/(?:www\.)?douyin\.com\/discover\?.*modal_id=(\d+)/,
|
47
|
+
needsRedirect: true
|
48
|
+
},
|
49
|
+
{
|
50
|
+
name: 'webapp',
|
51
|
+
// WebApp 連結,例如: https://www.douyin.com/webapp/7123456789
|
52
|
+
pattern: /https?:\/\/(?:www\.)?douyin\.com\/webapp\/(\d+)/,
|
53
|
+
needsRedirect: true
|
54
|
+
},
|
55
|
+
{
|
56
|
+
name: 'mobile-param',
|
57
|
+
// 帶參數的移動端連結,例如: https://www.iesdouyin.com/share/video/xxx/?region=CN&...
|
58
|
+
pattern: /https?:\/\/(?:www\.)?(?:douyin|iesdouyin)\.com\/.*\/.*\?.*(?:video_id|vid)=([^&]+)/,
|
59
|
+
needsRedirect: true
|
60
|
+
}
|
61
|
+
];
|
62
|
+
/**
|
63
|
+
* 創建用於在文本中查找所有抖音連結的正則表達式
|
64
|
+
* @returns RegExp 可以匹配任何抖音連結的正則表達式
|
65
|
+
*/
|
66
|
+
function createCombinedPattern() {
|
67
|
+
// 將所有模式的正則表達式合併為一個,使用 | 作為分隔符
|
68
|
+
const patternStrings = exports.DOUYIN_LINK_PATTERNS.map(pattern => {
|
69
|
+
// 移除 ^ 和 $ 錨點,因為我們要在文本中查找,而不是匹配整個字符串
|
70
|
+
return pattern.pattern.source.replace(/^\^|\$$/g, '');
|
71
|
+
});
|
72
|
+
// 建立一個新的正則表達式,使用 g 標誌進行全局匹配
|
73
|
+
const combinedPattern = new RegExp(patternStrings.join('|'), 'g');
|
74
|
+
logger_1.parserLogger.debug(`創建合併正則表達式: ${combinedPattern}`);
|
75
|
+
return combinedPattern;
|
76
|
+
}
|
77
|
+
/**
|
78
|
+
* 獲取抖音連結的類型
|
79
|
+
* @param url 抖音連結
|
80
|
+
* @returns LinkPattern | null 連結類型或 null (如果不匹配任何類型)
|
81
|
+
*/
|
82
|
+
function getLinkType(url) {
|
83
|
+
for (const pattern of exports.DOUYIN_LINK_PATTERNS) {
|
84
|
+
if (pattern.pattern.test(url)) {
|
85
|
+
logger_1.parserLogger.debug(`連結 ${url} 匹配類型: ${pattern.name}`);
|
86
|
+
return pattern;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
logger_1.parserLogger.debug(`連結 ${url} 不匹配任何已知類型`);
|
90
|
+
return null;
|
91
|
+
}
|
92
|
+
/**
|
93
|
+
* 從 URL 中提取影片 ID (如果可能)
|
94
|
+
* @param url 抖音連結
|
95
|
+
* @returns string | null 影片 ID 或 null (如果無法提取)
|
96
|
+
*/
|
97
|
+
function extractVideoId(url) {
|
98
|
+
for (const pattern of exports.DOUYIN_LINK_PATTERNS) {
|
99
|
+
const match = url.match(pattern.pattern);
|
100
|
+
if (match && match[1]) {
|
101
|
+
const videoId = match[1];
|
102
|
+
logger_1.parserLogger.debug(`從連結 ${url} 提取到影片 ID: ${videoId}`);
|
103
|
+
return videoId;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
// 嘗試從 URL 參數中提取 vid
|
107
|
+
try {
|
108
|
+
const urlObj = new URL(url);
|
109
|
+
const videoId = urlObj.searchParams.get('vid');
|
110
|
+
if (videoId) {
|
111
|
+
logger_1.parserLogger.debug(`從連結參數提取到影片 ID: ${videoId}`);
|
112
|
+
return videoId;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
catch (error) {
|
116
|
+
logger_1.parserLogger.debug(`URL 解析失敗: ${error}`);
|
117
|
+
}
|
118
|
+
logger_1.parserLogger.debug(`無法從連結 ${url} 提取影片 ID`);
|
119
|
+
return null;
|
120
|
+
}
|
121
|
+
//# sourceMappingURL=link-patterns.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"link-patterns.js","sourceRoot":"","sources":["../../../../src/douyin/parser/link-patterns.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/**
|
2
|
+
* 批量解析模組
|
3
|
+
* 並行處理多個抖音連結的解析
|
4
|
+
*/
|
5
|
+
import { ParseResult, ParseOptions } from '../../types';
|
6
|
+
/**
|
7
|
+
* 批量解析抖音連結
|
8
|
+
* @param urls 抖音連結陣列
|
9
|
+
* @param options 解析選項
|
10
|
+
* @returns Promise<ParseResult[]> 解析結果陣列
|
11
|
+
*/
|
12
|
+
export declare function parseBatch(urls: string[], options?: ParseOptions): Promise<ParseResult[]>;
|
13
|
+
/**
|
14
|
+
* 批量解析抖音連結,包含更詳細的進度和結果信息
|
15
|
+
* @param urls 抖音連結陣列
|
16
|
+
* @param options 解析選項
|
17
|
+
* @returns Promise<{ results: ParseResult[], stats: { total: number, success: number, failed: number } }>
|
18
|
+
*/
|
19
|
+
export declare function parseBatchWithStats(urls: string[], options?: ParseOptions): Promise<{
|
20
|
+
results: ParseResult[];
|
21
|
+
stats: {
|
22
|
+
total: number;
|
23
|
+
success: number;
|
24
|
+
failed: number;
|
25
|
+
};
|
26
|
+
}>;
|
@@ -0,0 +1,67 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/parser/parse-batch.ts
|
3
|
+
// Original file path: src/parser/parse-batch.ts
|
4
|
+
/**
|
5
|
+
* 批量解析模組
|
6
|
+
* 並行處理多個抖音連結的解析
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.parseBatch = parseBatch;
|
10
|
+
exports.parseBatchWithStats = parseBatchWithStats;
|
11
|
+
const logger_1 = require("../../utils/logger"); // Updated path to shared utils logger
|
12
|
+
const retry_1 = require("../../utils/retry"); // Updated path to shared utils
|
13
|
+
const resolve_links_1 = require("./resolve-links");
|
14
|
+
/**
|
15
|
+
* 批量解析抖音連結
|
16
|
+
* @param urls 抖音連結陣列
|
17
|
+
* @param options 解析選項
|
18
|
+
* @returns Promise<ParseResult[]> 解析結果陣列
|
19
|
+
*/
|
20
|
+
async function parseBatch(urls, options = {}) {
|
21
|
+
// 設置默認選項
|
22
|
+
const { concurrency = 3, retries = 3, userAgent, onProgress } = options;
|
23
|
+
logger_1.parserLogger.info(`開始批量解析 ${urls.length} 個抖音連結,並行數: ${concurrency}`);
|
24
|
+
if (urls.length === 0) {
|
25
|
+
logger_1.parserLogger.debug('沒有連結需要解析');
|
26
|
+
return [];
|
27
|
+
}
|
28
|
+
// 使用 withConcurrency 並行處理所有連結
|
29
|
+
const resolveOptions = { userAgent, retries };
|
30
|
+
// processBatch returns { results, errors }
|
31
|
+
const batchOutcome = await (0, retry_1.processBatch)(urls, async (url, index) => {
|
32
|
+
logger_1.parserLogger.debug(`解析連結 #${index + 1}/${urls.length}: ${url}`);
|
33
|
+
// resolveLink might return null on failure
|
34
|
+
return await (0, resolve_links_1.resolveLink)(url, resolveOptions);
|
35
|
+
}, {
|
36
|
+
concurrency,
|
37
|
+
retries,
|
38
|
+
// Pass the onProgress callback correctly to processBatch options
|
39
|
+
onProgress: onProgress ? (completed, total, errors) => onProgress(completed, total) : undefined
|
40
|
+
});
|
41
|
+
// Extract results and handle errors
|
42
|
+
const successfulResults = batchOutcome.results.filter((r) => r !== null); // Filter out nulls
|
43
|
+
const errors = batchOutcome.errors;
|
44
|
+
logger_1.parserLogger.info(`批量解析完成,成功解析 ${successfulResults.length}/${urls.length} 個連結`);
|
45
|
+
if (errors.length > 0) {
|
46
|
+
logger_1.parserLogger.warn(`解析過程中發生 ${errors.length} 個錯誤`);
|
47
|
+
}
|
48
|
+
return successfulResults;
|
49
|
+
}
|
50
|
+
/**
|
51
|
+
* 批量解析抖音連結,包含更詳細的進度和結果信息
|
52
|
+
* @param urls 抖音連結陣列
|
53
|
+
* @param options 解析選項
|
54
|
+
* @returns Promise<{ results: ParseResult[], stats: { total: number, success: number, failed: number } }>
|
55
|
+
*/
|
56
|
+
async function parseBatchWithStats(urls, options = {}) {
|
57
|
+
const results = await parseBatch(urls, options);
|
58
|
+
return {
|
59
|
+
results,
|
60
|
+
stats: {
|
61
|
+
total: urls.length,
|
62
|
+
success: results.length,
|
63
|
+
failed: urls.length - results.length
|
64
|
+
}
|
65
|
+
};
|
66
|
+
}
|
67
|
+
//# sourceMappingURL=parse-batch.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"parse-batch.js","sourceRoot":"","sources":["../../../../src/douyin/parser/parse-batch.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,gDAAgD;AAChD;;;GAGG;;AAaH,gCA0CC;AAQD,kDAqBC;AAlFD,+CAA4D,CAAC,sCAAsC;AACnG,6CAAiD,CAAC,+BAA+B;AAEjF,mDAA8C;AAE9C;;;;;GAKG;AACI,KAAK,UAAU,UAAU,CAC9B,IAAc,EACd,UAAwB,EAAE;IAE1B,SAAS;IACT,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAExE,qBAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,eAAe,WAAW,EAAE,CAAC,CAAC;IAE/D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,qBAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8BAA8B;IAC9B,MAAM,cAAc,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAC9C,2CAA2C;IAC3C,MAAM,YAAY,GAAG,MAAM,IAAA,oBAAY,EACrC,IAAI,EACJ,KAAK,EAAE,GAAW,EAAE,KAAa,EAA+B,EAAE;QAChE,qBAAM,CAAC,KAAK,CAAC,SAAS,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC;QAC1D,2CAA2C;QAC3C,OAAO,MAAM,IAAA,2BAAW,EAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC,EACD;QACE,WAAW;QACX,OAAO;QACP,iEAAiE;QACjE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;KAChG,CACF,CAAC;IAEF,oCAAoC;IACpC,MAAM,iBAAiB,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,mBAAmB;IAC/G,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;IAEnC,qBAAM,CAAC,IAAI,CAAC,eAAe,iBAAiB,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,MAAM,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,qBAAM,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,mBAAmB,CACvC,IAAc,EACd,UAAwB,EAAE;IAS1B,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEhD,OAAO;QACL,OAAO;QACP,KAAK,EAAE;YACL,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,OAAO,EAAE,OAAO,CAAC,MAAM;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM;SACrC;KACF,CAAC;AACJ,CAAC"}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
/**
|
2
|
+
* 抖音連結解析模組
|
3
|
+
* 從文本中解析抖音連結,支援多種格式並將其標準化
|
4
|
+
*
|
5
|
+
* @remarks
|
6
|
+
* 參考來源:此功能改寫自原本存放於 src/parser/resolve-links.ts 和 src/parser/parse-batch.ts 的功能
|
7
|
+
* 需要驗證:URL 處理邏輯是否與最新抖音 API 兼容
|
8
|
+
*/
|
9
|
+
import { ParseResult, ParseOptions } from '../../types';
|
10
|
+
/**
|
11
|
+
* 從文本中解析抖音連結,支援多種格式(標準URL、短連結、移動端分享連結等),並將其標準化
|
12
|
+
* @param text 包含一個或多個抖音連結的文本字串
|
13
|
+
* @param options 解析選項
|
14
|
+
* @returns 解析結果陣列
|
15
|
+
*
|
16
|
+
* @remarks
|
17
|
+
* 改寫自原本存放於 src/parser/index.ts 的 parseDouyinLinks 功能
|
18
|
+
* 但實現方式不同,此處使用了批量處理和並發控制
|
19
|
+
*/
|
20
|
+
export declare function parseDouyinLinks(text: string, options?: ParseOptions): Promise<ParseResult[]>;
|
21
|
+
/**
|
22
|
+
* 解析並標準化抖音連結
|
23
|
+
* @param url 抖音連結
|
24
|
+
* @returns 標準化後的解析結果
|
25
|
+
*
|
26
|
+
* @remarks
|
27
|
+
* 改寫自原本存放於 src/parser/resolve-links.ts 的功能
|
28
|
+
* 處理不同格式的抖音 URL 並解析 video ID
|
29
|
+
*/
|
30
|
+
export declare function resolveAndStandardizeDouyinLink(url: string): Promise<ParseResult | null>;
|
@@ -0,0 +1,164 @@
|
|
1
|
+
"use strict";
|
2
|
+
/**
|
3
|
+
* 抖音連結解析模組
|
4
|
+
* 從文本中解析抖音連結,支援多種格式並將其標準化
|
5
|
+
*
|
6
|
+
* @remarks
|
7
|
+
* 參考來源:此功能改寫自原本存放於 src/parser/resolve-links.ts 和 src/parser/parse-batch.ts 的功能
|
8
|
+
* 需要驗證:URL 處理邏輯是否與最新抖音 API 兼容
|
9
|
+
*/
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
11
|
+
exports.parseDouyinLinks = parseDouyinLinks;
|
12
|
+
exports.resolveAndStandardizeDouyinLink = resolveAndStandardizeDouyinLink;
|
13
|
+
const extractDouyinLinks_1 = require("./extractDouyinLinks");
|
14
|
+
/**
|
15
|
+
* 從文本中解析抖音連結,支援多種格式(標準URL、短連結、移動端分享連結等),並將其標準化
|
16
|
+
* @param text 包含一個或多個抖音連結的文本字串
|
17
|
+
* @param options 解析選項
|
18
|
+
* @returns 解析結果陣列
|
19
|
+
*
|
20
|
+
* @remarks
|
21
|
+
* 改寫自原本存放於 src/parser/index.ts 的 parseDouyinLinks 功能
|
22
|
+
* 但實現方式不同,此處使用了批量處理和並發控制
|
23
|
+
*/
|
24
|
+
async function parseDouyinLinks(text, options = {}) {
|
25
|
+
// 提取所有抖音連結
|
26
|
+
const links = (0, extractDouyinLinks_1.extractDouyinLinks)(text);
|
27
|
+
if (links.length === 0) {
|
28
|
+
return [];
|
29
|
+
}
|
30
|
+
const { concurrency = 3, onProgress } = options;
|
31
|
+
const results = [];
|
32
|
+
const total = links.length;
|
33
|
+
let processed = 0;
|
34
|
+
// 分批處理連結,避免同時發送過多請求
|
35
|
+
for (let i = 0; i < links.length; i += concurrency) {
|
36
|
+
const batch = links.slice(i, i + concurrency);
|
37
|
+
const batchResults = await Promise.all(batch.map(async (link) => {
|
38
|
+
try {
|
39
|
+
return await resolveAndStandardizeDouyinLink(link);
|
40
|
+
}
|
41
|
+
catch (error) {
|
42
|
+
console.error(`解析連結失敗: ${link}`, error);
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
finally {
|
46
|
+
processed++;
|
47
|
+
if (onProgress) {
|
48
|
+
onProgress(processed, total);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}));
|
52
|
+
// 過濾掉 null 結果並添加到結果集
|
53
|
+
results.push(...batchResults.filter((r) => r !== null));
|
54
|
+
}
|
55
|
+
return results;
|
56
|
+
}
|
57
|
+
/**
|
58
|
+
* 解析並標準化抖音連結
|
59
|
+
* @param url 抖音連結
|
60
|
+
* @returns 標準化後的解析結果
|
61
|
+
*
|
62
|
+
* @remarks
|
63
|
+
* 改寫自原本存放於 src/parser/resolve-links.ts 的功能
|
64
|
+
* 處理不同格式的抖音 URL 並解析 video ID
|
65
|
+
*/
|
66
|
+
async function resolveAndStandardizeDouyinLink(url) {
|
67
|
+
try {
|
68
|
+
// 處理短連結
|
69
|
+
let expandedUrl = url;
|
70
|
+
if (url.includes('v.douyin.com') || url.includes('iesdouyin.com')) {
|
71
|
+
const resolvedUrl = await expandShortUrl(url);
|
72
|
+
// 如果展開成功,使用展開後的 URL
|
73
|
+
if (resolvedUrl) {
|
74
|
+
expandedUrl = resolvedUrl;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
// 提取影片 ID
|
78
|
+
const id = extractVideoId(expandedUrl);
|
79
|
+
if (!id) {
|
80
|
+
console.warn(`無法從連結提取影片 ID: ${url}`);
|
81
|
+
return null;
|
82
|
+
}
|
83
|
+
// 創建標準 URL
|
84
|
+
const standardUrl = createStandardUrl(id);
|
85
|
+
return {
|
86
|
+
id,
|
87
|
+
standardUrl,
|
88
|
+
originalUrl: url,
|
89
|
+
type: 'video'
|
90
|
+
};
|
91
|
+
}
|
92
|
+
catch (error) {
|
93
|
+
console.error(`標準化連結失敗: ${url}`, error);
|
94
|
+
return null;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
/**
|
98
|
+
* 展開抖音短連結
|
99
|
+
* @param shortUrl 抖音短連結
|
100
|
+
* @returns 展開後的完整 URL
|
101
|
+
*
|
102
|
+
* @remarks
|
103
|
+
* 改寫自原本存放於 src/parser/resolve-links.ts 的功能
|
104
|
+
* 處理抖音短網址重定向
|
105
|
+
*/
|
106
|
+
async function expandShortUrl(shortUrl) {
|
107
|
+
try {
|
108
|
+
const response = await fetch(shortUrl, { method: 'HEAD', redirect: 'follow' });
|
109
|
+
return response.url || null;
|
110
|
+
}
|
111
|
+
catch (error) {
|
112
|
+
console.error(`展開短連結失敗: ${shortUrl}`, error);
|
113
|
+
return null;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
/**
|
117
|
+
* 從抖音 URL 中提取影片 ID
|
118
|
+
* @param url 抖音 URL
|
119
|
+
* @returns 影片 ID
|
120
|
+
*
|
121
|
+
* @remarks
|
122
|
+
* 改寫自原本存放於 src/parser/link-patterns.ts 的功能
|
123
|
+
*/
|
124
|
+
function extractVideoId(url) {
|
125
|
+
// 標準網頁 URL
|
126
|
+
const videoIdMatch = url.match(/douyin\.com\/video\/(\d+)/i);
|
127
|
+
if (videoIdMatch && videoIdMatch[1]) {
|
128
|
+
return videoIdMatch[1];
|
129
|
+
}
|
130
|
+
// 筆記連結
|
131
|
+
const noteMatch = url.match(/douyin\.com\/note\/(\d+)/i);
|
132
|
+
if (noteMatch && noteMatch[1]) {
|
133
|
+
return noteMatch[1];
|
134
|
+
}
|
135
|
+
// 分享連結
|
136
|
+
const shareMatch = url.match(/douyin\.com\/share\/video\/(\d+)/i);
|
137
|
+
if (shareMatch && shareMatch[1]) {
|
138
|
+
return shareMatch[1];
|
139
|
+
}
|
140
|
+
// 嘗試從 URL 參數中提取
|
141
|
+
try {
|
142
|
+
const url_obj = new URL(url);
|
143
|
+
const videoId = url_obj.searchParams.get('video_id') || url_obj.searchParams.get('vid');
|
144
|
+
if (videoId) {
|
145
|
+
return videoId;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
catch (e) {
|
149
|
+
// URL 解析失敗,忽略錯誤
|
150
|
+
}
|
151
|
+
return null;
|
152
|
+
}
|
153
|
+
/**
|
154
|
+
* 根據影片 ID 創建標準抖音 URL
|
155
|
+
* @param id 影片 ID
|
156
|
+
* @returns 標準抖音 URL
|
157
|
+
*
|
158
|
+
* @remarks
|
159
|
+
* 改寫自原本存放於 src/parser/resolve-links.ts 的功能
|
160
|
+
*/
|
161
|
+
function createStandardUrl(id) {
|
162
|
+
return `https://www.douyin.com/video/${id}`;
|
163
|
+
}
|
164
|
+
//# sourceMappingURL=parseDouyinLinks.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"parseDouyinLinks.js","sourceRoot":"","sources":["../../../../src/douyin/parser/parseDouyinLinks.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/**
|
2
|
+
* 連結解析與標準化模組
|
3
|
+
* 解析抖音連結並將其標準化
|
4
|
+
*/
|
5
|
+
import { ParseResult, ParseOptions } from '../../types';
|
6
|
+
/**
|
7
|
+
* 標準化抖音影片 URL
|
8
|
+
* @param videoId 影片 ID
|
9
|
+
* @returns 標準化後的抖音影片 URL
|
10
|
+
*/
|
11
|
+
export declare function createStandardUrl(videoId: string): string;
|
12
|
+
/**
|
13
|
+
* 解析抖音短連結並獲取標準 URL
|
14
|
+
* @param shortUrl 抖音短連結
|
15
|
+
* @param options 解析選項
|
16
|
+
* @returns Promise<string | null> 標準抖音影片 URL 或 null
|
17
|
+
*/
|
18
|
+
export declare function resolveShortUrl(shortUrl: string, options?: Pick<ParseOptions, 'userAgent' | 'retries'>): Promise<string | null>;
|
19
|
+
/**
|
20
|
+
* 解析單個抖音連結
|
21
|
+
* @param url 抖音連結
|
22
|
+
* @param options 解析選項
|
23
|
+
* @returns Promise<ParseResult | null> 解析結果或 null
|
24
|
+
*/
|
25
|
+
export declare function resolveLink(url: string, options?: Pick<ParseOptions, 'userAgent' | 'retries'>): Promise<ParseResult | null>;
|
@@ -0,0 +1,131 @@
|
|
1
|
+
"use strict";
|
2
|
+
// Copied from src/parser/resolve-links.ts
|
3
|
+
// Original file path: src/parser/resolve-links.ts
|
4
|
+
/**
|
5
|
+
* 連結解析與標準化模組
|
6
|
+
* 解析抖音連結並將其標準化
|
7
|
+
*/
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.createStandardUrl = createStandardUrl;
|
10
|
+
exports.resolveShortUrl = resolveShortUrl;
|
11
|
+
exports.resolveLink = resolveLink;
|
12
|
+
const logger_1 = require("../../utils/logger"); // Updated path to shared utils logger
|
13
|
+
const fetch_1 = require("../../utils/fetch"); // Updated path to shared utils
|
14
|
+
const retry_1 = require("../../utils/retry"); // Updated path to shared utils
|
15
|
+
const link_patterns_1 = require("./link-patterns");
|
16
|
+
/**
|
17
|
+
* 標準化抖音影片 URL
|
18
|
+
* @param videoId 影片 ID
|
19
|
+
* @returns 標準化後的抖音影片 URL
|
20
|
+
*/
|
21
|
+
function createStandardUrl(videoId) {
|
22
|
+
return `https://www.douyin.com/video/${videoId}`;
|
23
|
+
}
|
24
|
+
/**
|
25
|
+
* 解析抖音短連結並獲取標準 URL
|
26
|
+
* @param shortUrl 抖音短連結
|
27
|
+
* @param options 解析選項
|
28
|
+
* @returns Promise<string | null> 標準抖音影片 URL 或 null
|
29
|
+
*/
|
30
|
+
async function resolveShortUrl(shortUrl, options = {}) {
|
31
|
+
const { userAgent, retries = 3 } = options;
|
32
|
+
try {
|
33
|
+
logger_1.parserLogger.debug(`解析短連結: ${shortUrl}`);
|
34
|
+
// 使用 withRetry 包裝請求,以便在失敗時自動重試
|
35
|
+
const finalUrl = await (0, retry_1.withRetry)(async () => {
|
36
|
+
const response = await (0, fetch_1.enhancedFetch)(shortUrl, {
|
37
|
+
redirect: 'follow', // 自動跟隨重定向
|
38
|
+
headers: {
|
39
|
+
'User-Agent': userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
|
40
|
+
}
|
41
|
+
});
|
42
|
+
return response.url; // 返回最終 URL
|
43
|
+
}, { retries });
|
44
|
+
logger_1.parserLogger.debug(`解析後的最終 URL: ${finalUrl}`);
|
45
|
+
// 嘗試從最終 URL 中提取影片 ID
|
46
|
+
const videoId = (0, link_patterns_1.extractVideoId)(finalUrl);
|
47
|
+
if (videoId) {
|
48
|
+
const standardUrl = createStandardUrl(videoId);
|
49
|
+
logger_1.parserLogger.debug(`已提取標準 URL: ${standardUrl}`);
|
50
|
+
return standardUrl;
|
51
|
+
}
|
52
|
+
if (finalUrl.startsWith('https://www.iesdouyin.com/share/video/')) {
|
53
|
+
const response = await (0, fetch_1.enhancedFetch)(shortUrl, {
|
54
|
+
redirect: 'follow', // 自動跟隨重定向
|
55
|
+
headers: {
|
56
|
+
'User-Agent': userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
|
57
|
+
}
|
58
|
+
});
|
59
|
+
return response.url;
|
60
|
+
}
|
61
|
+
logger_1.parserLogger.warn(`無法從 URL (${finalUrl}) 提取影片 ID`);
|
62
|
+
return null;
|
63
|
+
}
|
64
|
+
catch (error) {
|
65
|
+
logger_1.parserLogger.error(`解析短連結 ${shortUrl} 失敗:`, error instanceof Error ? error : new Error(String(error)));
|
66
|
+
return null;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
/**
|
70
|
+
* 解析單個抖音連結
|
71
|
+
* @param url 抖音連結
|
72
|
+
* @param options 解析選項
|
73
|
+
* @returns Promise<ParseResult | null> 解析結果或 null
|
74
|
+
*/
|
75
|
+
async function resolveLink(url, options = {}) {
|
76
|
+
try {
|
77
|
+
// 獲取連結類型
|
78
|
+
const linkType = (0, link_patterns_1.getLinkType)(url);
|
79
|
+
// 如果連結類型未知,則返回 null
|
80
|
+
if (!linkType) {
|
81
|
+
logger_1.parserLogger.warn(`未知的連結類型: ${url}`);
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
// 處理不同類型的連結
|
85
|
+
if (linkType.needsRedirect) {
|
86
|
+
// 需要跟隨重定向的連結
|
87
|
+
const standardUrl = await resolveShortUrl(url, options);
|
88
|
+
if (!standardUrl) {
|
89
|
+
return null;
|
90
|
+
}
|
91
|
+
// 從標準 URL 提取影片 ID
|
92
|
+
const videoId = (0, link_patterns_1.extractVideoId)(standardUrl);
|
93
|
+
if (!videoId) {
|
94
|
+
return null;
|
95
|
+
}
|
96
|
+
// Determine link type from the resolved standard URL
|
97
|
+
const finalLinkType = (0, link_patterns_1.getLinkType)(standardUrl);
|
98
|
+
logger_1.parserLogger.debug(`標準化連結 (重定向後): ${standardUrl} (類型: ${finalLinkType?.name || 'unknown'})`); // Use name property
|
99
|
+
return {
|
100
|
+
id: videoId,
|
101
|
+
standardUrl,
|
102
|
+
originalUrl: url,
|
103
|
+
type: finalLinkType?.name === 'standard' ? 'video' : (finalLinkType?.name ? 'unknown' : 'unknown') // Map name to ParseResult type
|
104
|
+
// TODO: Need a better mapping if user links are introduced
|
105
|
+
};
|
106
|
+
}
|
107
|
+
else {
|
108
|
+
// 已經是標準連結,直接提取影片 ID
|
109
|
+
const videoId = (0, link_patterns_1.extractVideoId)(url);
|
110
|
+
if (!videoId) {
|
111
|
+
return null;
|
112
|
+
}
|
113
|
+
const standardUrl = createStandardUrl(videoId);
|
114
|
+
// Determine link type from the standard URL
|
115
|
+
const finalLinkType = (0, link_patterns_1.getLinkType)(standardUrl);
|
116
|
+
logger_1.parserLogger.debug(`標準化連結 (直接): ${standardUrl} (類型: ${finalLinkType?.name || 'unknown'})`); // Use name property
|
117
|
+
return {
|
118
|
+
id: videoId,
|
119
|
+
standardUrl,
|
120
|
+
originalUrl: url,
|
121
|
+
type: finalLinkType?.name === 'standard' ? 'video' : (finalLinkType?.name ? 'unknown' : 'unknown') // Map name to ParseResult type
|
122
|
+
// TODO: Need a better mapping if user links are introduced
|
123
|
+
};
|
124
|
+
}
|
125
|
+
}
|
126
|
+
catch (error) {
|
127
|
+
logger_1.parserLogger.error(`解析連結 ${url} 失敗:`, error instanceof Error ? error : new Error(String(error)));
|
128
|
+
return null;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
//# sourceMappingURL=resolve-links.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"resolve-links.js","sourceRoot":"","sources":["../../../../src/douyin/parser/resolve-links.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* 抖音影片解析與下載套件
|
3
|
+
*
|
4
|
+
* @packageDocumentation
|
5
|
+
*
|
6
|
+
* @remarks
|
7
|
+
* 免責聲明:本套件僅供學習和個人使用。請遵守抖音平台的服務條款和相關法規。
|
8
|
+
* 用戶須對使用本套件的行為負責,開發者不對任何濫用行為或因此產生的後果負責。
|
9
|
+
* 請尊重原創作者的著作權,不要未經授權分發下載的內容。
|
10
|
+
*/
|
11
|
+
export * from './types';
|
12
|
+
export { AppError, ErrorCode } from './utils/error';
|
13
|
+
export * as douyin from './douyin';
|
14
|
+
export { Logger, LogLevel } from './utils/logger';
|
15
|
+
export { PerformanceMonitor, timeAsync, time } from './utils/performance';
|
16
|
+
export { browserManager, cleanupBrowser } from './utils/browser';
|