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.
Files changed (161) hide show
  1. package/README.md +114 -0
  2. package/dist/bin/dydl.d.ts +2 -0
  3. package/dist/bin/dydl.js +17 -0
  4. package/dist/bin/dydl.js.map +1 -0
  5. package/dist/src/cli/commands/check.d.ts +10 -0
  6. package/dist/src/cli/commands/check.js +102 -0
  7. package/dist/src/cli/commands/check.js.map +1 -0
  8. package/dist/src/cli/commands/info.d.ts +10 -0
  9. package/dist/src/cli/commands/info.js +106 -0
  10. package/dist/src/cli/commands/info.js.map +1 -0
  11. package/dist/src/cli/commands/list.d.ts +10 -0
  12. package/dist/src/cli/commands/list.js +85 -0
  13. package/dist/src/cli/commands/list.js.map +1 -0
  14. package/dist/src/cli/commands/login.d.ts +10 -0
  15. package/dist/src/cli/commands/login.js +158 -0
  16. package/dist/src/cli/commands/login.js.map +1 -0
  17. package/dist/src/cli/commands/open.d.ts +10 -0
  18. package/dist/src/cli/commands/open.js +141 -0
  19. package/dist/src/cli/commands/open.js.map +1 -0
  20. package/dist/src/cli/commands/video.d.ts +10 -0
  21. package/dist/src/cli/commands/video.js +209 -0
  22. package/dist/src/cli/commands/video.js.map +1 -0
  23. package/dist/src/cli/index.d.ts +15 -0
  24. package/dist/src/cli/index.js +134 -0
  25. package/dist/src/cli/index.js.map +1 -0
  26. package/dist/src/cli/progress-display.d.ts +80 -0
  27. package/dist/src/cli/progress-display.js +225 -0
  28. package/dist/src/cli/progress-display.js.map +1 -0
  29. package/dist/src/cli/utils.d.ts +31 -0
  30. package/dist/src/cli/utils.js +171 -0
  31. package/dist/src/cli/utils.js.map +1 -0
  32. package/dist/src/douyin/auth/cookie-path.d.ts +22 -0
  33. package/dist/src/douyin/auth/cookie-path.js +72 -0
  34. package/dist/src/douyin/auth/cookie-path.js.map +1 -0
  35. package/dist/src/douyin/auth/cookie-storage.d.ts +19 -0
  36. package/dist/src/douyin/auth/cookie-storage.js +65 -0
  37. package/dist/src/douyin/auth/cookie-storage.js.map +1 -0
  38. package/dist/src/douyin/auth/errors.d.ts +28 -0
  39. package/dist/src/douyin/auth/errors.js +49 -0
  40. package/dist/src/douyin/auth/errors.js.map +1 -0
  41. package/dist/src/douyin/auth/getDefaultCookiePath.d.ts +24 -0
  42. package/dist/src/douyin/auth/getDefaultCookiePath.js +90 -0
  43. package/dist/src/douyin/auth/getDefaultCookiePath.js.map +1 -0
  44. package/dist/src/douyin/auth/index.d.ts +16 -0
  45. package/dist/src/douyin/auth/index.js +68 -0
  46. package/dist/src/douyin/auth/index.js.map +1 -0
  47. package/dist/src/douyin/auth/isValidCookie.d.ts +18 -0
  48. package/dist/src/douyin/auth/isValidCookie.js +60 -0
  49. package/dist/src/douyin/auth/isValidCookie.js.map +1 -0
  50. package/dist/src/douyin/auth/loadAndValidateCookie.d.ts +17 -0
  51. package/dist/src/douyin/auth/loadAndValidateCookie.js +45 -0
  52. package/dist/src/douyin/auth/loadAndValidateCookie.js.map +1 -0
  53. package/dist/src/douyin/auth/loadCookie.d.ts +17 -0
  54. package/dist/src/douyin/auth/loadCookie.js +79 -0
  55. package/dist/src/douyin/auth/loadCookie.js.map +1 -0
  56. package/dist/src/douyin/auth/login.d.ts +33 -0
  57. package/dist/src/douyin/auth/login.js +157 -0
  58. package/dist/src/douyin/auth/login.js.map +1 -0
  59. package/dist/src/douyin/auth/saveCookie.d.ts +17 -0
  60. package/dist/src/douyin/auth/saveCookie.js +89 -0
  61. package/dist/src/douyin/auth/saveCookie.js.map +1 -0
  62. package/dist/src/douyin/auth/validate.d.ts +11 -0
  63. package/dist/src/douyin/auth/validate.js +104 -0
  64. package/dist/src/douyin/auth/validate.js.map +1 -0
  65. package/dist/src/douyin/browser/manager.d.ts +54 -0
  66. package/dist/src/douyin/browser/manager.js +344 -0
  67. package/dist/src/douyin/browser/manager.js.map +1 -0
  68. package/dist/src/douyin/download/download-manager.d.ts +25 -0
  69. package/dist/src/douyin/download/download-manager.js +107 -0
  70. package/dist/src/douyin/download/download-manager.js.map +1 -0
  71. package/dist/src/douyin/download/error-handler.d.ts +49 -0
  72. package/dist/src/douyin/download/error-handler.js +160 -0
  73. package/dist/src/douyin/download/error-handler.js.map +1 -0
  74. package/dist/src/douyin/download/index.d.ts +39 -0
  75. package/dist/src/douyin/download/index.js +156 -0
  76. package/dist/src/douyin/download/index.js.map +1 -0
  77. package/dist/src/douyin/download/path-formatter.d.ts +42 -0
  78. package/dist/src/douyin/download/path-formatter.js +107 -0
  79. package/dist/src/douyin/download/path-formatter.js.map +1 -0
  80. package/dist/src/douyin/download/video-downloader.d.ts +35 -0
  81. package/dist/src/douyin/download/video-downloader.js +223 -0
  82. package/dist/src/douyin/download/video-downloader.js.map +1 -0
  83. package/dist/src/douyin/index.d.ts +19 -0
  84. package/dist/src/douyin/index.js +52 -0
  85. package/dist/src/douyin/index.js.map +1 -0
  86. package/dist/src/douyin/info/batch-processor.d.ts +15 -0
  87. package/dist/src/douyin/info/batch-processor.js +65 -0
  88. package/dist/src/douyin/info/batch-processor.js.map +1 -0
  89. package/dist/src/douyin/info/browser-manager.d.ts +56 -0
  90. package/dist/src/douyin/info/browser-manager.js +225 -0
  91. package/dist/src/douyin/info/browser-manager.js.map +1 -0
  92. package/dist/src/douyin/info/error-handler.d.ts +36 -0
  93. package/dist/src/douyin/info/error-handler.js +172 -0
  94. package/dist/src/douyin/info/error-handler.js.map +1 -0
  95. package/dist/src/douyin/info/fetch-video-detail.d.ts +14 -0
  96. package/dist/src/douyin/info/fetch-video-detail.js +247 -0
  97. package/dist/src/douyin/info/fetch-video-detail.js.map +1 -0
  98. package/dist/src/douyin/info/index.d.ts +29 -0
  99. package/dist/src/douyin/info/index.js +85 -0
  100. package/dist/src/douyin/info/index.js.map +1 -0
  101. package/dist/src/douyin/info/text-processor.d.ts +15 -0
  102. package/dist/src/douyin/info/text-processor.js +47 -0
  103. package/dist/src/douyin/info/text-processor.js.map +1 -0
  104. package/dist/src/douyin/info/user.d.ts +26 -0
  105. package/dist/src/douyin/info/user.js +237 -0
  106. package/dist/src/douyin/info/user.js.map +1 -0
  107. package/dist/src/douyin/parser/containsDouyinLink.d.ts +18 -0
  108. package/dist/src/douyin/parser/containsDouyinLink.js +27 -0
  109. package/dist/src/douyin/parser/containsDouyinLink.js.map +1 -0
  110. package/dist/src/douyin/parser/extract-links.d.ts +23 -0
  111. package/dist/src/douyin/parser/extract-links.js +79 -0
  112. package/dist/src/douyin/parser/extract-links.js.map +1 -0
  113. package/dist/src/douyin/parser/extractDouyinLinks.d.ts +18 -0
  114. package/dist/src/douyin/parser/extractDouyinLinks.js +58 -0
  115. package/dist/src/douyin/parser/extractDouyinLinks.js.map +1 -0
  116. package/dist/src/douyin/parser/index.d.ts +35 -0
  117. package/dist/src/douyin/parser/index.js +70 -0
  118. package/dist/src/douyin/parser/index.js.map +1 -0
  119. package/dist/src/douyin/parser/link-patterns.d.ts +34 -0
  120. package/dist/src/douyin/parser/link-patterns.js +121 -0
  121. package/dist/src/douyin/parser/link-patterns.js.map +1 -0
  122. package/dist/src/douyin/parser/parse-batch.d.ts +26 -0
  123. package/dist/src/douyin/parser/parse-batch.js +67 -0
  124. package/dist/src/douyin/parser/parse-batch.js.map +1 -0
  125. package/dist/src/douyin/parser/parseDouyinLinks.d.ts +30 -0
  126. package/dist/src/douyin/parser/parseDouyinLinks.js +164 -0
  127. package/dist/src/douyin/parser/parseDouyinLinks.js.map +1 -0
  128. package/dist/src/douyin/parser/resolve-links.d.ts +25 -0
  129. package/dist/src/douyin/parser/resolve-links.js +131 -0
  130. package/dist/src/douyin/parser/resolve-links.js.map +1 -0
  131. package/dist/src/index.d.ts +16 -0
  132. package/dist/src/index.js +72 -0
  133. package/dist/src/index.js.map +1 -0
  134. package/dist/src/types.d.ts +217 -0
  135. package/dist/src/types.js +6 -0
  136. package/dist/src/types.js.map +1 -0
  137. package/dist/src/utils/browser.d.ts +73 -0
  138. package/dist/src/utils/browser.js +96 -0
  139. package/dist/src/utils/browser.js.map +1 -0
  140. package/dist/src/utils/error.d.ts +160 -0
  141. package/dist/src/utils/error.js +334 -0
  142. package/dist/src/utils/error.js.map +1 -0
  143. package/dist/src/utils/fetch.d.ts +41 -0
  144. package/dist/src/utils/fetch.js +155 -0
  145. package/dist/src/utils/fetch.js.map +1 -0
  146. package/dist/src/utils/file.d.ts +46 -0
  147. package/dist/src/utils/file.js +189 -0
  148. package/dist/src/utils/file.js.map +1 -0
  149. package/dist/src/utils/index.d.ts +11 -0
  150. package/dist/src/utils/index.js +29 -0
  151. package/dist/src/utils/index.js.map +1 -0
  152. package/dist/src/utils/logger.d.ts +161 -0
  153. package/dist/src/utils/logger.js +286 -0
  154. package/dist/src/utils/logger.js.map +1 -0
  155. package/dist/src/utils/performance.d.ts +98 -0
  156. package/dist/src/utils/performance.js +292 -0
  157. package/dist/src/utils/performance.js.map +1 -0
  158. package/dist/src/utils/retry.d.ts +56 -0
  159. package/dist/src/utils/retry.js +127 -0
  160. package/dist/src/utils/retry.js.map +1 -0
  161. 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":";AAAA,0CAA0C;AAC1C,kDAAkD;AAClD;;;GAGG;;;AAkEH,sDAYC;AAOD,kCAUC;AAOD,wCAwBC;AA5HD,+CAA4D,CAAC,iBAAiB;AAW9E;;;GAGG;AACU,QAAA,oBAAoB,GAAkB;IACjD;QACE,IAAI,EAAE,UAAU;QAChB,qDAAqD;QACrD,OAAO,EAAE,gDAAgD;QACzD,aAAa,EAAE,KAAK;KACrB;IACD;QACE,IAAI,EAAE,OAAO;QACb,2EAA2E;QAC3E,OAAO,EAAE,+CAA+C;QACxD,aAAa,EAAE,IAAI;KACpB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,2DAA2D;QAC3D,OAAO,EAAE,uDAAuD;QAChE,aAAa,EAAE,IAAI;KACpB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,mDAAmD;QACnD,OAAO,EAAE,+CAA+C;QACxD,aAAa,EAAE,IAAI;KACpB;IACD;QACE,IAAI,EAAE,UAAU;QAChB,gEAAgE;QAChE,OAAO,EAAE,8DAA8D;QACvE,aAAa,EAAE,IAAI;KACpB;IACD;QACE,IAAI,EAAE,QAAQ;QACd,yDAAyD;QACzD,OAAO,EAAE,iDAAiD;QAC1D,aAAa,EAAE,IAAI;KACpB;IACD;QACE,IAAI,EAAE,cAAc;QACpB,yEAAyE;QACzE,OAAO,EAAE,oFAAoF;QAC7F,aAAa,EAAE,IAAI;KACpB;CACF,CAAC;AAEF;;;GAGG;AACH,SAAgB,qBAAqB;IACnC,8BAA8B;IAC9B,MAAM,cAAc,GAAG,4BAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;QACxD,qCAAqC;QACrC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,MAAM,eAAe,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,qBAAM,CAAC,KAAK,CAAC,cAAc,eAAe,EAAE,CAAC,CAAC;IAE9C,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,GAAW;IACrC,KAAK,MAAM,OAAO,IAAI,4BAAoB,EAAE,CAAC;QAC3C,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,qBAAM,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAED,qBAAM,CAAC,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,GAAW;IACxC,KAAK,MAAM,OAAO,IAAI,4BAAoB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACzB,qBAAM,CAAC,KAAK,CAAC,OAAO,GAAG,cAAc,OAAO,EAAE,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,OAAO,EAAE,CAAC;YACZ,qBAAM,CAAC,KAAK,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;YAC1C,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qBAAM,CAAC,KAAK,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,qBAAM,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -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';