patreon-dl 1.0.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 (223) hide show
  1. package/README.md +422 -0
  2. package/bin/patreon-dl.js +5 -0
  3. package/dist/cli/CLIOptionValidator.d.ts +9 -0
  4. package/dist/cli/CLIOptionValidator.d.ts.map +1 -0
  5. package/dist/cli/CLIOptionValidator.js +85 -0
  6. package/dist/cli/CLIOptionValidator.js.map +1 -0
  7. package/dist/cli/CLIOptions.d.ts +20 -0
  8. package/dist/cli/CLIOptions.d.ts.map +1 -0
  9. package/dist/cli/CLIOptions.js +75 -0
  10. package/dist/cli/CLIOptions.js.map +1 -0
  11. package/dist/cli/CommandLineParser.d.ts +11 -0
  12. package/dist/cli/CommandLineParser.d.ts.map +1 -0
  13. package/dist/cli/CommandLineParser.js +212 -0
  14. package/dist/cli/CommandLineParser.js.map +1 -0
  15. package/dist/cli/ConfigFileParser.d.ts +9 -0
  16. package/dist/cli/ConfigFileParser.d.ts.map +1 -0
  17. package/dist/cli/ConfigFileParser.js +163 -0
  18. package/dist/cli/ConfigFileParser.js.map +1 -0
  19. package/dist/cli/index.d.ts +7 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +162 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/downloaders/Bootstrap.d.ts +29 -0
  24. package/dist/downloaders/Bootstrap.d.ts.map +1 -0
  25. package/dist/downloaders/Bootstrap.js +51 -0
  26. package/dist/downloaders/Bootstrap.js.map +1 -0
  27. package/dist/downloaders/Downloader.d.ts +59 -0
  28. package/dist/downloaders/Downloader.d.ts.map +1 -0
  29. package/dist/downloaders/Downloader.js +357 -0
  30. package/dist/downloaders/Downloader.js.map +1 -0
  31. package/dist/downloaders/DownloaderEvent.d.ts +47 -0
  32. package/dist/downloaders/DownloaderEvent.d.ts.map +1 -0
  33. package/dist/downloaders/DownloaderEvent.js +6 -0
  34. package/dist/downloaders/DownloaderEvent.js.map +1 -0
  35. package/dist/downloaders/DownloaderOptions.d.ts +39 -0
  36. package/dist/downloaders/DownloaderOptions.d.ts.map +1 -0
  37. package/dist/downloaders/DownloaderOptions.js +69 -0
  38. package/dist/downloaders/DownloaderOptions.js.map +1 -0
  39. package/dist/downloaders/PostDownloader.d.ts +8 -0
  40. package/dist/downloaders/PostDownloader.d.ts.map +1 -0
  41. package/dist/downloaders/PostDownloader.js +428 -0
  42. package/dist/downloaders/PostDownloader.js.map +1 -0
  43. package/dist/downloaders/ProductDownloader.d.ts +8 -0
  44. package/dist/downloaders/ProductDownloader.d.ts.map +1 -0
  45. package/dist/downloaders/ProductDownloader.js +171 -0
  46. package/dist/downloaders/ProductDownloader.js.map +1 -0
  47. package/dist/downloaders/cache/StatusCache.d.ts +43 -0
  48. package/dist/downloaders/cache/StatusCache.d.ts.map +1 -0
  49. package/dist/downloaders/cache/StatusCache.js +206 -0
  50. package/dist/downloaders/cache/StatusCache.js.map +1 -0
  51. package/dist/downloaders/index.d.ts +7 -0
  52. package/dist/downloaders/index.d.ts.map +1 -0
  53. package/dist/downloaders/index.js +6 -0
  54. package/dist/downloaders/index.js.map +1 -0
  55. package/dist/downloaders/task/DownloadTask.d.ts +89 -0
  56. package/dist/downloaders/task/DownloadTask.d.ts.map +1 -0
  57. package/dist/downloaders/task/DownloadTask.js +240 -0
  58. package/dist/downloaders/task/DownloadTask.js.map +1 -0
  59. package/dist/downloaders/task/DownloadTaskBatch.d.ts +45 -0
  60. package/dist/downloaders/task/DownloadTaskBatch.d.ts.map +1 -0
  61. package/dist/downloaders/task/DownloadTaskBatch.js +195 -0
  62. package/dist/downloaders/task/DownloadTaskBatch.js.map +1 -0
  63. package/dist/downloaders/task/DownloadTaskBatchEvent.d.ts +32 -0
  64. package/dist/downloaders/task/DownloadTaskBatchEvent.d.ts.map +1 -0
  65. package/dist/downloaders/task/DownloadTaskBatchEvent.js +2 -0
  66. package/dist/downloaders/task/DownloadTaskBatchEvent.js.map +1 -0
  67. package/dist/downloaders/task/DownloadTaskFactory.d.ts +20 -0
  68. package/dist/downloaders/task/DownloadTaskFactory.d.ts.map +1 -0
  69. package/dist/downloaders/task/DownloadTaskFactory.js +177 -0
  70. package/dist/downloaders/task/DownloadTaskFactory.js.map +1 -0
  71. package/dist/downloaders/task/FFmpegDownloadTask.d.ts +27 -0
  72. package/dist/downloaders/task/FFmpegDownloadTask.d.ts.map +1 -0
  73. package/dist/downloaders/task/FFmpegDownloadTask.js +206 -0
  74. package/dist/downloaders/task/FFmpegDownloadTask.js.map +1 -0
  75. package/dist/downloaders/task/FetcherDownloadTask.d.ts +21 -0
  76. package/dist/downloaders/task/FetcherDownloadTask.d.ts.map +1 -0
  77. package/dist/downloaders/task/FetcherDownloadTask.js +213 -0
  78. package/dist/downloaders/task/FetcherDownloadTask.js.map +1 -0
  79. package/dist/downloaders/task/index.d.ts +4 -0
  80. package/dist/downloaders/task/index.d.ts.map +1 -0
  81. package/dist/downloaders/task/index.js +3 -0
  82. package/dist/downloaders/task/index.js.map +1 -0
  83. package/dist/downloaders/templates/CampaignInfo.d.ts +3 -0
  84. package/dist/downloaders/templates/CampaignInfo.d.ts.map +1 -0
  85. package/dist/downloaders/templates/CampaignInfo.js +58 -0
  86. package/dist/downloaders/templates/CampaignInfo.js.map +1 -0
  87. package/dist/downloaders/templates/PostInfo.d.ts +4 -0
  88. package/dist/downloaders/templates/PostInfo.d.ts.map +1 -0
  89. package/dist/downloaders/templates/PostInfo.js +45 -0
  90. package/dist/downloaders/templates/PostInfo.js.map +1 -0
  91. package/dist/downloaders/templates/ProductInfo.d.ts +3 -0
  92. package/dist/downloaders/templates/ProductInfo.d.ts.map +1 -0
  93. package/dist/downloaders/templates/ProductInfo.js +20 -0
  94. package/dist/downloaders/templates/ProductInfo.js.map +1 -0
  95. package/dist/entities/Attachment.d.ts +7 -0
  96. package/dist/entities/Attachment.d.ts.map +1 -0
  97. package/dist/entities/Attachment.js +2 -0
  98. package/dist/entities/Attachment.js.map +1 -0
  99. package/dist/entities/Campaign.d.ts +19 -0
  100. package/dist/entities/Campaign.d.ts.map +1 -0
  101. package/dist/entities/Campaign.js +2 -0
  102. package/dist/entities/Campaign.js.map +1 -0
  103. package/dist/entities/Downloadable.d.ts +6 -0
  104. package/dist/entities/Downloadable.d.ts.map +1 -0
  105. package/dist/entities/Downloadable.js +5 -0
  106. package/dist/entities/Downloadable.js.map +1 -0
  107. package/dist/entities/MediaItem.d.ts +95 -0
  108. package/dist/entities/MediaItem.d.ts.map +1 -0
  109. package/dist/entities/MediaItem.js +2 -0
  110. package/dist/entities/MediaItem.js.map +1 -0
  111. package/dist/entities/Post.d.ts +87 -0
  112. package/dist/entities/Post.d.ts.map +1 -0
  113. package/dist/entities/Post.js +2 -0
  114. package/dist/entities/Post.js.map +1 -0
  115. package/dist/entities/Product.d.ts +17 -0
  116. package/dist/entities/Product.d.ts.map +1 -0
  117. package/dist/entities/Product.js +2 -0
  118. package/dist/entities/Product.js.map +1 -0
  119. package/dist/entities/Reward.d.ts +14 -0
  120. package/dist/entities/Reward.d.ts.map +1 -0
  121. package/dist/entities/Reward.js +2 -0
  122. package/dist/entities/Reward.js.map +1 -0
  123. package/dist/entities/User.d.ts +15 -0
  124. package/dist/entities/User.d.ts.map +1 -0
  125. package/dist/entities/User.js +2 -0
  126. package/dist/entities/User.js.map +1 -0
  127. package/dist/entities/index.d.ts +9 -0
  128. package/dist/entities/index.d.ts.map +1 -0
  129. package/dist/entities/index.js +6 -0
  130. package/dist/entities/index.js.map +1 -0
  131. package/dist/index.d.ts +8 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +8 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/parsers/PageParser.d.ts +6 -0
  136. package/dist/parsers/PageParser.d.ts.map +1 -0
  137. package/dist/parsers/PageParser.js +23 -0
  138. package/dist/parsers/PageParser.js.map +1 -0
  139. package/dist/parsers/Parser.d.ts +43 -0
  140. package/dist/parsers/Parser.d.ts.map +1 -0
  141. package/dist/parsers/Parser.js +439 -0
  142. package/dist/parsers/Parser.js.map +1 -0
  143. package/dist/parsers/PostParser.d.ts +7 -0
  144. package/dist/parsers/PostParser.d.ts.map +1 -0
  145. package/dist/parsers/PostParser.js +259 -0
  146. package/dist/parsers/PostParser.js.map +1 -0
  147. package/dist/parsers/ProductParser.d.ts +7 -0
  148. package/dist/parsers/ProductParser.d.ts.map +1 -0
  149. package/dist/parsers/ProductParser.js +70 -0
  150. package/dist/parsers/ProductParser.js.map +1 -0
  151. package/dist/utils/AttachmentFilenameResolver.d.ts +9 -0
  152. package/dist/utils/AttachmentFilenameResolver.d.ts.map +1 -0
  153. package/dist/utils/AttachmentFilenameResolver.js +73 -0
  154. package/dist/utils/AttachmentFilenameResolver.js.map +1 -0
  155. package/dist/utils/FSHelper.d.ts +57 -0
  156. package/dist/utils/FSHelper.d.ts.map +1 -0
  157. package/dist/utils/FSHelper.js +214 -0
  158. package/dist/utils/FSHelper.js.map +1 -0
  159. package/dist/utils/Fetcher.d.ts +45 -0
  160. package/dist/utils/Fetcher.d.ts.map +1 -0
  161. package/dist/utils/Fetcher.js +192 -0
  162. package/dist/utils/Fetcher.js.map +1 -0
  163. package/dist/utils/FetcherProgressMonitor.d.ts +18 -0
  164. package/dist/utils/FetcherProgressMonitor.d.ts.map +1 -0
  165. package/dist/utils/FetcherProgressMonitor.js +56 -0
  166. package/dist/utils/FetcherProgressMonitor.js.map +1 -0
  167. package/dist/utils/FilenameFormatHelper.d.ts +44 -0
  168. package/dist/utils/FilenameFormatHelper.d.ts.map +1 -0
  169. package/dist/utils/FilenameFormatHelper.js +98 -0
  170. package/dist/utils/FilenameFormatHelper.js.map +1 -0
  171. package/dist/utils/FllenameResolver.d.ts +20 -0
  172. package/dist/utils/FllenameResolver.d.ts.map +1 -0
  173. package/dist/utils/FllenameResolver.js +55 -0
  174. package/dist/utils/FllenameResolver.js.map +1 -0
  175. package/dist/utils/Formatter.d.ts +21 -0
  176. package/dist/utils/Formatter.d.ts.map +1 -0
  177. package/dist/utils/Formatter.js +112 -0
  178. package/dist/utils/Formatter.js.map +1 -0
  179. package/dist/utils/MediaFilenameResolver.d.ts +9 -0
  180. package/dist/utils/MediaFilenameResolver.d.ts.map +1 -0
  181. package/dist/utils/MediaFilenameResolver.js +90 -0
  182. package/dist/utils/MediaFilenameResolver.js.map +1 -0
  183. package/dist/utils/Misc.d.ts +14 -0
  184. package/dist/utils/Misc.d.ts.map +1 -0
  185. package/dist/utils/Misc.js +4 -0
  186. package/dist/utils/Misc.js.map +1 -0
  187. package/dist/utils/ObjectHelper.d.ts +4 -0
  188. package/dist/utils/ObjectHelper.d.ts.map +1 -0
  189. package/dist/utils/ObjectHelper.js +30 -0
  190. package/dist/utils/ObjectHelper.js.map +1 -0
  191. package/dist/utils/PackageInfo.d.ts +10 -0
  192. package/dist/utils/PackageInfo.d.ts.map +1 -0
  193. package/dist/utils/PackageInfo.js +33 -0
  194. package/dist/utils/PackageInfo.js.map +1 -0
  195. package/dist/utils/URLHelper.d.ts +40 -0
  196. package/dist/utils/URLHelper.d.ts.map +1 -0
  197. package/dist/utils/URLHelper.js +192 -0
  198. package/dist/utils/URLHelper.js.map +1 -0
  199. package/dist/utils/index.d.ts +2 -0
  200. package/dist/utils/index.d.ts.map +1 -0
  201. package/dist/utils/index.js +2 -0
  202. package/dist/utils/index.js.map +1 -0
  203. package/dist/utils/logging/ChainLogger.d.ts +11 -0
  204. package/dist/utils/logging/ChainLogger.d.ts.map +1 -0
  205. package/dist/utils/logging/ChainLogger.js +50 -0
  206. package/dist/utils/logging/ChainLogger.js.map +1 -0
  207. package/dist/utils/logging/ConsoleLogger.d.ts +31 -0
  208. package/dist/utils/logging/ConsoleLogger.d.ts.map +1 -0
  209. package/dist/utils/logging/ConsoleLogger.js +126 -0
  210. package/dist/utils/logging/ConsoleLogger.js.map +1 -0
  211. package/dist/utils/logging/FileLogger.d.ts +26 -0
  212. package/dist/utils/logging/FileLogger.d.ts.map +1 -0
  213. package/dist/utils/logging/FileLogger.js +147 -0
  214. package/dist/utils/logging/FileLogger.js.map +1 -0
  215. package/dist/utils/logging/Logger.d.ts +12 -0
  216. package/dist/utils/logging/Logger.d.ts.map +1 -0
  217. package/dist/utils/logging/Logger.js +15 -0
  218. package/dist/utils/logging/Logger.js.map +1 -0
  219. package/dist/utils/logging/index.d.ts +7 -0
  220. package/dist/utils/logging/index.d.ts.map +1 -0
  221. package/dist/utils/logging/index.js +7 -0
  222. package/dist/utils/logging/index.js.map +1 -0
  223. package/package.json +78 -0
@@ -0,0 +1,177 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _a, _DownloadTaskFactory_pickVariant;
7
+ import path from 'path';
8
+ import MediaFilenameResolver from '../../utils/MediaFilenameResolver.js';
9
+ import FetcherDownloadTask from './FetcherDownloadTask.js';
10
+ import AttachmentFilenameResolver from '../../utils/AttachmentFilenameResolver.js';
11
+ const DEFAULT_IMAGE_URL_PRIORITY = [
12
+ 'original',
13
+ 'default',
14
+ 'download',
15
+ 'defaultSmall',
16
+ 'thumbnailLarge',
17
+ 'thumbnail',
18
+ 'thumbnailSmall'
19
+ ];
20
+ const CAMPAIGN_COVER_PHOTO_URL_PRIORITY = [
21
+ 'xlarge',
22
+ 'large',
23
+ 'medium',
24
+ 'small',
25
+ 'xsmall'
26
+ ];
27
+ const POST_COVER_IMAGE_URL_PRIORITY = [
28
+ 'large',
29
+ 'default',
30
+ 'thumb',
31
+ 'thumbSquareLarge',
32
+ 'thumbSquare'
33
+ ];
34
+ const POST_THUMBNAIL_URL_PRIORITY = [
35
+ 'large2',
36
+ 'default',
37
+ 'large',
38
+ 'square'
39
+ ];
40
+ const NULL_VARIANT = '/*NULL*/';
41
+ export default class DownloadTaskFactory {
42
+ static createFromDownloadable(params) {
43
+ const { item, destDir, fetcher, destFilenameFormat, fileExistsAction, downloadAllVariants, maxRetries, callbacks, logger } = params;
44
+ const __getSrcURLs = () => {
45
+ if (item.type === 'image') {
46
+ let urls = {};
47
+ let priority = [];
48
+ switch (item.imageType) {
49
+ case 'single':
50
+ return {
51
+ [`${NULL_VARIANT}`]: item.imageURL
52
+ };
53
+ case 'default':
54
+ urls = {
55
+ ...item.imageURLs,
56
+ download: item.downloadURL
57
+ };
58
+ priority = DEFAULT_IMAGE_URL_PRIORITY;
59
+ break;
60
+ case 'campaignCoverPhoto':
61
+ urls = item.imageURLs;
62
+ priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;
63
+ break;
64
+ case 'postCoverImage':
65
+ urls = item.imageURLs;
66
+ priority = POST_COVER_IMAGE_URL_PRIORITY;
67
+ break;
68
+ case 'postThumbnail':
69
+ urls = item.imageURLs;
70
+ priority = POST_THUMBNAIL_URL_PRIORITY;
71
+ break;
72
+ }
73
+ if (downloadAllVariants) {
74
+ return urls;
75
+ }
76
+ return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
77
+ }
78
+ else if (item.type === 'video') {
79
+ let videoVariantName = 'display';
80
+ if (item.size.width && item.size.height) {
81
+ videoVariantName = `${item.size.width}x${item.size.height}`;
82
+ }
83
+ const urls = {
84
+ download: item.downloadURL,
85
+ [`${videoVariantName}`]: item.displayURLs.video
86
+ };
87
+ if (downloadAllVariants) {
88
+ return urls;
89
+ }
90
+ const priority = ['download', videoVariantName];
91
+ return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
92
+ }
93
+ else if (item.type === 'audio') {
94
+ return {
95
+ [`${NULL_VARIANT}`]: item.url
96
+ };
97
+ }
98
+ else if (item.type === 'file') {
99
+ return {
100
+ [`${NULL_VARIANT}`]: item.downloadURL
101
+ };
102
+ }
103
+ else if (item.type === 'dummy') {
104
+ return item.srcURLs;
105
+ }
106
+ else if (item.type === 'attachment') {
107
+ return {
108
+ [`${NULL_VARIANT}`]: item.url
109
+ };
110
+ }
111
+ return {};
112
+ };
113
+ const srcURLs = __getSrcURLs();
114
+ const tasks = [];
115
+ for (const [variant, url] of Object.entries(srcURLs)) {
116
+ const destFilenameResolver = item.type === 'attachment' ?
117
+ new AttachmentFilenameResolver(item, url, destFilenameFormat) :
118
+ new MediaFilenameResolver(item, url, destFilenameFormat, variant !== NULL_VARIANT ? variant : null, downloadAllVariants);
119
+ if (url) {
120
+ tasks.push(new FetcherDownloadTask({
121
+ fetcher,
122
+ src: url,
123
+ maxRetries,
124
+ destDir,
125
+ destFilenameResolver,
126
+ fileExistsAction,
127
+ srcEntity: item,
128
+ callbacks: callbacks || null,
129
+ logger
130
+ }));
131
+ }
132
+ }
133
+ // Create a DownloadTask backed by a DummyMediaItem for video thumbnail
134
+ if (item.type === 'video' && item.displayURLs.thumbnail) {
135
+ let filename = null;
136
+ if (item.filename) {
137
+ // Video file extension not applicable to thumbnail, so strip it.
138
+ filename = path.parse(item.filename).name;
139
+ }
140
+ const videoThumbnailMediaItem = {
141
+ type: 'dummy',
142
+ id: item.id,
143
+ filename,
144
+ mimeType: null,
145
+ srcURLs: {
146
+ thumbnail: item.displayURLs.thumbnail
147
+ }
148
+ };
149
+ tasks.push(...this.createFromDownloadable({
150
+ item: videoThumbnailMediaItem,
151
+ destDir,
152
+ fetcher,
153
+ destFilenameFormat,
154
+ fileExistsAction,
155
+ downloadAllVariants: true,
156
+ maxRetries,
157
+ callbacks,
158
+ logger
159
+ }));
160
+ }
161
+ if (tasks.length === 0) {
162
+ throw Error('No src URL found');
163
+ }
164
+ return tasks;
165
+ }
166
+ }
167
+ _a = DownloadTaskFactory, _DownloadTaskFactory_pickVariant = function _DownloadTaskFactory_pickVariant(urls, priority) {
168
+ for (const variant of priority) {
169
+ if (urls[variant]) {
170
+ return {
171
+ [`${variant}`]: urls[variant]
172
+ };
173
+ }
174
+ }
175
+ return {};
176
+ };
177
+ //# sourceMappingURL=DownloadTaskFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DownloadTaskFactory.js","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTaskFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { DummyMediaItem } from '../../entities/MediaItem.js';\nimport Fetcher from '../../utils/Fetcher.js';\nimport MediaFilenameResolver from '../../utils/MediaFilenameResolver.js';\nimport DownloadTask, { DownloadTaskCallbacks } from './DownloadTask.js';\nimport FetcherDownloadTask from './FetcherDownloadTask.js';\nimport { Downloadable } from '../../entities/Downloadable.js';\nimport AttachmentFilenameResolver from '../../utils/AttachmentFilenameResolver.js';\nimport { FileExistsAction } from '../DownloaderOptions.js';\nimport Logger from '../../utils/logging/Logger.js';\n\nconst DEFAULT_IMAGE_URL_PRIORITY = [\n 'original',\n 'default',\n 'download',\n 'defaultSmall',\n 'thumbnailLarge',\n 'thumbnail',\n 'thumbnailSmall'\n];\n\nconst CAMPAIGN_COVER_PHOTO_URL_PRIORITY = [\n 'xlarge',\n 'large',\n 'medium',\n 'small',\n 'xsmall'\n];\n\nconst POST_COVER_IMAGE_URL_PRIORITY = [\n 'large',\n 'default',\n 'thumb',\n 'thumbSquareLarge',\n 'thumbSquare'\n];\n\nconst POST_THUMBNAIL_URL_PRIORITY = [\n 'large2',\n 'default',\n 'large',\n 'square'\n];\n\nconst NULL_VARIANT = '/*NULL*/';\n\nexport default class DownloadTaskFactory {\n\n static createFromDownloadable(params: {\n item: Downloadable,\n destDir: string,\n fetcher: Fetcher,\n destFilenameFormat: string,\n fileExistsAction: FileExistsAction,\n downloadAllVariants: boolean,\n maxRetries: number,\n callbacks?: DownloadTaskCallbacks | null,\n logger?: Logger | null;\n }) {\n\n const {\n item,\n destDir,\n fetcher,\n destFilenameFormat,\n fileExistsAction,\n downloadAllVariants,\n maxRetries,\n callbacks,\n logger\n } = params;\n\n const __getSrcURLs = () => {\n if (item.type === 'image') {\n let urls: Record<string, string | null> = {};\n let priority: string[] = [];\n switch (item.imageType) {\n case 'single':\n return {\n [`${NULL_VARIANT}`]: item.imageURL\n };\n case 'default':\n urls = {\n ...item.imageURLs,\n download: item.downloadURL\n };\n priority = DEFAULT_IMAGE_URL_PRIORITY;\n break;\n case 'campaignCoverPhoto':\n urls = item.imageURLs;\n priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;\n break;\n case 'postCoverImage':\n urls = item.imageURLs;\n priority = POST_COVER_IMAGE_URL_PRIORITY;\n break;\n case 'postThumbnail':\n urls = item.imageURLs;\n priority = POST_THUMBNAIL_URL_PRIORITY;\n break;\n }\n if (downloadAllVariants) {\n return urls;\n }\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'video') {\n let videoVariantName = 'display';\n if (item.size.width && item.size.height) {\n videoVariantName = `${item.size.width}x${item.size.height}`;\n }\n const urls = {\n download: item.downloadURL,\n [`${videoVariantName}`]: item.displayURLs.video\n };\n if (downloadAllVariants) {\n return urls;\n }\n const priority = [ 'download', videoVariantName ];\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'audio') {\n return {\n [`${NULL_VARIANT}`]: item.url\n };\n }\n else if (item.type === 'file') {\n return {\n [`${NULL_VARIANT}`]: item.downloadURL\n };\n }\n else if (item.type === 'dummy') {\n return item.srcURLs;\n }\n else if (item.type === 'attachment') {\n return {\n [`${NULL_VARIANT}`]: item.url\n };\n }\n return {};\n };\n\n\n const srcURLs = __getSrcURLs();\n\n const tasks: DownloadTask[] = [];\n for (const [ variant, url ] of Object.entries(srcURLs)) {\n const destFilenameResolver = item.type === 'attachment' ?\n new AttachmentFilenameResolver(item, url, destFilenameFormat) :\n new MediaFilenameResolver(item, url, destFilenameFormat,\n variant !== NULL_VARIANT ? variant : null, downloadAllVariants);\n\n if (url) {\n tasks.push(new FetcherDownloadTask<Downloadable>({\n fetcher,\n src: url,\n maxRetries,\n destDir,\n destFilenameResolver,\n fileExistsAction,\n srcEntity: item,\n callbacks: callbacks || null,\n logger\n }));\n }\n }\n\n // Create a DownloadTask backed by a DummyMediaItem for video thumbnail\n if (item.type === 'video' && item.displayURLs.thumbnail) {\n let filename: string | null = null;\n if (item.filename) {\n // Video file extension not applicable to thumbnail, so strip it.\n filename = path.parse(item.filename).name;\n }\n const videoThumbnailMediaItem: DummyMediaItem = {\n type: 'dummy',\n id: item.id,\n filename,\n mimeType: null,\n srcURLs: {\n thumbnail: item.displayURLs.thumbnail\n }\n };\n tasks.push(\n ...this.createFromDownloadable({\n item: videoThumbnailMediaItem,\n destDir,\n fetcher,\n destFilenameFormat,\n fileExistsAction,\n downloadAllVariants: true, // Ensure variant name ('thumbnail') appears in dest filename\n maxRetries,\n callbacks,\n logger\n })\n );\n }\n\n if (tasks.length === 0) {\n throw Error('No src URL found');\n }\n\n return tasks;\n }\n\n static #pickVariant(urls: Record<string, any>, priority: string[]) {\n for (const variant of priority) {\n if (urls[variant]) {\n return {\n [`${variant}`]: urls[variant]\n };\n }\n }\n return {};\n }\n\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import DownloadTask, { DownloadTaskParams } from './DownloadTask.js';
2
+ import { VideoMediaItem } from '../../entities/MediaItem.js';
3
+ import { FileExistsAction } from './../DownloaderOptions.js';
4
+ export interface FFmpegDownloadTaskParams extends DownloadTaskParams<VideoMediaItem> {
5
+ srcEntity: VideoMediaItem;
6
+ destFilePath: string;
7
+ fileExistsAction: FileExistsAction;
8
+ }
9
+ export interface FFmpegProgress {
10
+ frames: number;
11
+ currentFps: number;
12
+ currentKbps: number;
13
+ targetSize: number;
14
+ timemark: string;
15
+ percent: number;
16
+ }
17
+ export default class FFmpegDownloadTask extends DownloadTask<VideoMediaItem> {
18
+ #private;
19
+ protected name: string;
20
+ constructor(params: FFmpegDownloadTaskParams);
21
+ protected doStart(): Promise<void>;
22
+ protected doAbort(): Promise<void>;
23
+ protected doDestroy(): Promise<void>;
24
+ protected doGetProgress(): undefined;
25
+ get commandLine(): string | null;
26
+ }
27
+ //# sourceMappingURL=FFmpegDownloadTask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FFmpegDownloadTask.d.ts","sourceRoot":"","sources":["../../../src/downloaders/task/FFmpegDownloadTask.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,EAAE,EAAoB,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB,CAAC,cAAc,CAAC;IAClF,SAAS,EAAE,cAAc,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAGD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,YAAY,CAAC,cAAc,CAAC;;IAE1E,SAAS,CAAC,IAAI,SAAwB;gBAU1B,MAAM,EAAE,wBAAwB;IAU5C,SAAS,CAAC,OAAO;cA0HD,OAAO;cAgBP,SAAS;IAKzB,SAAS,CAAC,aAAa;IAIvB,IAAI,WAAW,kBAEd;CAuDF"}
@@ -0,0 +1,206 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _FFmpegDownloadTask_instances, _FFmpegDownloadTask_destFilePath, _FFmpegDownloadTask_fileExistsAction, _FFmpegDownloadTask_ffmpegCommand, _FFmpegDownloadTask_commandLine, _FFmpegDownloadTask_abortController, _FFmpegDownloadTask_abortingCallback, _FFmpegDownloadTask_convertFFmpegProgress, _FFmpegDownloadTask_timemarkToSeconds;
13
+ import fs from 'fs';
14
+ import ffmpeg from 'fluent-ffmpeg';
15
+ import DownloadTask from './DownloadTask.js';
16
+ import FSHelper from '../../utils/FSHelper.js';
17
+ export default class FFmpegDownloadTask extends DownloadTask {
18
+ constructor(params) {
19
+ super(params);
20
+ _FFmpegDownloadTask_instances.add(this);
21
+ this.name = 'FFmpegDownloadTask';
22
+ _FFmpegDownloadTask_destFilePath.set(this, void 0);
23
+ _FFmpegDownloadTask_fileExistsAction.set(this, void 0);
24
+ _FFmpegDownloadTask_ffmpegCommand.set(this, void 0);
25
+ _FFmpegDownloadTask_commandLine.set(this, void 0);
26
+ _FFmpegDownloadTask_abortController.set(this, void 0);
27
+ _FFmpegDownloadTask_abortingCallback.set(this, void 0);
28
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_destFilePath, params.destFilePath, "f");
29
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_fileExistsAction, params.fileExistsAction, "f");
30
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_commandLine, null, "f");
31
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortController, null, "f");
32
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortingCallback, null, "f");
33
+ }
34
+ doStart() {
35
+ return new Promise((resolve) => {
36
+ if (this.status === 'aborted') {
37
+ resolve();
38
+ }
39
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortController, new AbortController(), "f");
40
+ this.resolvedDestPath = __classPrivateFieldGet(this, _FFmpegDownloadTask_destFilePath, "f");
41
+ let lastDownloadedFilePath = null;
42
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_fileExistsAction, "f") === 'saveAsCopy' || __classPrivateFieldGet(this, _FFmpegDownloadTask_fileExistsAction, "f") === 'saveAsCopyIfNewer') {
43
+ const { filePath, preceding } = FSHelper.checkFileExistsAndIncrement(__classPrivateFieldGet(this, _FFmpegDownloadTask_destFilePath, "f"));
44
+ this.resolvedDestPath = filePath;
45
+ lastDownloadedFilePath = preceding;
46
+ }
47
+ else if (__classPrivateFieldGet(this, _FFmpegDownloadTask_fileExistsAction, "f") === 'skip' && fs.existsSync(__classPrivateFieldGet(this, _FFmpegDownloadTask_destFilePath, "f"))) {
48
+ this.notifySkip({
49
+ name: 'destFileExists',
50
+ message: `Destination file exists (${__classPrivateFieldGet(this, _FFmpegDownloadTask_destFilePath, "f")})`,
51
+ existingDestFilePath: __classPrivateFieldGet(this, _FFmpegDownloadTask_destFilePath, "f")
52
+ });
53
+ resolve();
54
+ return;
55
+ }
56
+ const destPath = this.resolvedDestPath;
57
+ const tmpFilePath = `${destPath}.part`;
58
+ const __cleanup = () => {
59
+ if (fs.existsSync(tmpFilePath)) {
60
+ this.log('debug', `Clean up ${tmpFilePath}`);
61
+ fs.unlinkSync(tmpFilePath);
62
+ }
63
+ };
64
+ try {
65
+ let hasError = false;
66
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_ffmpegCommand, ffmpeg(this.src)
67
+ .inputOption('-protocol_whitelist file,http,https,tcp,tls')
68
+ .outputOption('-c copy')
69
+ .outputOption('-f mp4')
70
+ .output(tmpFilePath), "f");
71
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").on('start', (commandLine) => {
72
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_commandLine, commandLine, "f");
73
+ this.notifyStart();
74
+ });
75
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").on('progress', (progress) => {
76
+ this.notifyProgress(__classPrivateFieldGet(this, _FFmpegDownloadTask_instances, "m", _FFmpegDownloadTask_convertFFmpegProgress).call(this, progress));
77
+ });
78
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").on('end', async () => {
79
+ if (!hasError) {
80
+ let proceedWithCommit = true;
81
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_fileExistsAction, "f") === 'saveAsCopyIfNewer' && lastDownloadedFilePath && fs.existsSync(lastDownloadedFilePath)) {
82
+ // Compare checksum of downloaded file with that of last downloaded
83
+ const compareMsg = `(saveAsCopyIfNewer) Compare "${tmpFilePath}" with "${lastDownloadedFilePath}"`;
84
+ proceedWithCommit = !(await FSHelper.compareFiles(tmpFilePath, lastDownloadedFilePath));
85
+ if (!proceedWithCommit) {
86
+ this.log('debug', `${compareMsg}: Files match`);
87
+ this.notifySkip({
88
+ name: 'destFileExists',
89
+ message: `Destination file exists with same content (${lastDownloadedFilePath})`,
90
+ existingDestFilePath: lastDownloadedFilePath
91
+ });
92
+ }
93
+ else {
94
+ this.log('debug', `${compareMsg}: Files do not match`);
95
+ }
96
+ }
97
+ if (proceedWithCommit) {
98
+ this.log('debug', `Commit "${tmpFilePath}" to "${destPath}; filesize: ${fs.lstatSync(tmpFilePath).size} bytes`);
99
+ fs.renameSync(tmpFilePath, destPath);
100
+ this.notifyComplete();
101
+ }
102
+ __cleanup();
103
+ resolve();
104
+ }
105
+ });
106
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").on('error', (error) => {
107
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_abortingCallback, "f")) {
108
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_abortingCallback, "f").call(this);
109
+ }
110
+ else {
111
+ hasError = true;
112
+ this.notifyError(error);
113
+ }
114
+ __cleanup();
115
+ resolve();
116
+ });
117
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_abortController, "f").signal.onabort = () => {
118
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f")) {
119
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").kill('SIGKILL');
120
+ }
121
+ else if (__classPrivateFieldGet(this, _FFmpegDownloadTask_abortingCallback, "f")) {
122
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_abortingCallback, "f").call(this);
123
+ __cleanup();
124
+ resolve();
125
+ }
126
+ };
127
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").run();
128
+ }
129
+ catch (error) {
130
+ this.notifyError(error);
131
+ __cleanup();
132
+ resolve();
133
+ }
134
+ })
135
+ .finally(() => {
136
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f")) {
137
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_ffmpegCommand, "f").removeAllListeners();
138
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_ffmpegCommand, null, "f");
139
+ }
140
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortController, null, "f");
141
+ });
142
+ }
143
+ async doAbort() {
144
+ return new Promise((resolve) => {
145
+ if (__classPrivateFieldGet(this, _FFmpegDownloadTask_abortController, "f")) {
146
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortingCallback, () => {
147
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_abortingCallback, null, "f");
148
+ this.notifyAbort();
149
+ resolve();
150
+ }, "f");
151
+ __classPrivateFieldGet(this, _FFmpegDownloadTask_abortController, "f").abort();
152
+ }
153
+ else {
154
+ resolve();
155
+ }
156
+ });
157
+ }
158
+ async doDestroy() {
159
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_destFilePath, '', "f");
160
+ __classPrivateFieldSet(this, _FFmpegDownloadTask_commandLine, null, "f");
161
+ }
162
+ doGetProgress() {
163
+ return undefined;
164
+ }
165
+ get commandLine() {
166
+ return __classPrivateFieldGet(this, _FFmpegDownloadTask_commandLine, "f");
167
+ }
168
+ }
169
+ _FFmpegDownloadTask_destFilePath = new WeakMap(), _FFmpegDownloadTask_fileExistsAction = new WeakMap(), _FFmpegDownloadTask_ffmpegCommand = new WeakMap(), _FFmpegDownloadTask_commandLine = new WeakMap(), _FFmpegDownloadTask_abortController = new WeakMap(), _FFmpegDownloadTask_abortingCallback = new WeakMap(), _FFmpegDownloadTask_instances = new WeakSet(), _FFmpegDownloadTask_convertFFmpegProgress = function _FFmpegDownloadTask_convertFFmpegProgress(progress) {
170
+ const video = this.srcEntity;
171
+ if (video.duration && this.resolvedDestFilename && this.resolvedDestPath) {
172
+ const length = video.duration;
173
+ const downloaded = __classPrivateFieldGet(this, _FFmpegDownloadTask_instances, "m", _FFmpegDownloadTask_timemarkToSeconds).call(this, progress.timemark);
174
+ return {
175
+ destFilename: this.resolvedDestFilename,
176
+ destFilePath: this.resolvedDestPath,
177
+ lengthUnit: 'second',
178
+ length: Number(length.toFixed(2)),
179
+ lengthDownloaded: Number(downloaded.toFixed(2)),
180
+ percent: Number((downloaded / length * 100).toFixed(2)),
181
+ sizeDownloaded: Number(progress.targetSize.toFixed(2)),
182
+ speed: Number(progress.currentKbps.toFixed(2))
183
+ };
184
+ }
185
+ return null;
186
+ }, _FFmpegDownloadTask_timemarkToSeconds = function _FFmpegDownloadTask_timemarkToSeconds(timemark) {
187
+ if (typeof timemark === 'number') {
188
+ return timemark;
189
+ }
190
+ if (timemark.indexOf(':') === -1 && timemark.indexOf('.') >= 0) {
191
+ return Number(timemark);
192
+ }
193
+ const parts = timemark.split(':');
194
+ // Add seconds
195
+ let secs = Number(parts.pop());
196
+ if (parts.length) {
197
+ // Add minutes
198
+ secs += Number(parts.pop()) * 60;
199
+ }
200
+ if (parts.length) {
201
+ // Add hours
202
+ secs += Number(parts.pop()) * 3600;
203
+ }
204
+ return secs;
205
+ };
206
+ //# sourceMappingURL=FFmpegDownloadTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FFmpegDownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/FFmpegDownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg';\nimport DownloadTask, { DownloadProgress, DownloadTaskParams } from './DownloadTask.js';\nimport { VideoMediaItem } from '../../entities/MediaItem.js';\nimport { FileExistsAction } from './../DownloaderOptions.js';\nimport FSHelper from '../../utils/FSHelper.js';\n\nexport interface FFmpegDownloadTaskParams extends DownloadTaskParams<VideoMediaItem> {\n srcEntity: VideoMediaItem;\n destFilePath: string;\n fileExistsAction: FileExistsAction;\n}\n\n// https://github.com/fluent-ffmpeg/node-fluent-ffmpeg\nexport interface FFmpegProgress {\n frames: number; // Total processed frame count\n currentFps: number; // Framerate at which FFmpeg is currently processing\n currentKbps: number; // Throughput at which FFmpeg is currently processing\n targetSize: number; // Current size of the target file in kilobytes\n timemark: string; // The timestamp of the current frame e.g '00:02:23.08'\n percent: number;\n}\n\nexport default class FFmpegDownloadTask extends DownloadTask<VideoMediaItem> {\n\n protected name = 'FFmpegDownloadTask';\n\n #destFilePath: string;\n #fileExistsAction: FileExistsAction;\n #ffmpegCommand: FfmpegCommand | null;\n #commandLine: string | null;\n\n #abortController: AbortController | null;\n #abortingCallback: (() => void) | null;\n\n constructor(params: FFmpegDownloadTaskParams) {\n super(params);\n this.#destFilePath = params.destFilePath;\n this.#fileExistsAction = params.fileExistsAction;\n this.#commandLine = null;\n\n this.#abortController = null;\n this.#abortingCallback = null;\n }\n\n protected doStart() {\n return new Promise<void>((resolve) => {\n\n if (this.status === 'aborted') {\n resolve();\n }\n\n this.#abortController = new AbortController();\n\n this.resolvedDestPath = this.#destFilePath;\n\n let lastDownloadedFilePath: string | null = null;\n if (this.#fileExistsAction === 'saveAsCopy' || this.#fileExistsAction === 'saveAsCopyIfNewer') {\n const { filePath, preceding } = FSHelper.checkFileExistsAndIncrement(this.#destFilePath);\n this.resolvedDestPath = filePath;\n lastDownloadedFilePath = preceding;\n }\n else if (this.#fileExistsAction === 'skip' && fs.existsSync(this.#destFilePath)) {\n this.notifySkip({\n name: 'destFileExists',\n message: `Destination file exists (${this.#destFilePath})`,\n existingDestFilePath: this.#destFilePath\n });\n resolve();\n return;\n }\n\n const destPath = this.resolvedDestPath;\n const tmpFilePath = `${destPath}.part`;\n const __cleanup = () => {\n if (fs.existsSync(tmpFilePath)) {\n this.log('debug', `Clean up ${tmpFilePath}`);\n fs.unlinkSync(tmpFilePath);\n }\n };\n\n try {\n let hasError = false;\n this.#ffmpegCommand = ffmpeg(this.src)\n .inputOption('-protocol_whitelist file,http,https,tcp,tls')\n .outputOption('-c copy')\n .outputOption('-f mp4')\n .output(tmpFilePath);\n\n this.#ffmpegCommand.on('start', (commandLine: string) => {\n this.#commandLine = commandLine;\n this.notifyStart();\n });\n\n this.#ffmpegCommand.on('progress', (progress: FFmpegProgress) => {\n this.notifyProgress(this.#convertFFmpegProgress(progress));\n });\n\n this.#ffmpegCommand.on('end', async () => {\n if (!hasError) {\n let proceedWithCommit = true;\n if (this.#fileExistsAction === 'saveAsCopyIfNewer' && lastDownloadedFilePath && fs.existsSync(lastDownloadedFilePath)) {\n // Compare checksum of downloaded file with that of last downloaded\n const compareMsg = `(saveAsCopyIfNewer) Compare \"${tmpFilePath}\" with \"${lastDownloadedFilePath}\"`;\n proceedWithCommit = !(await FSHelper.compareFiles(tmpFilePath, lastDownloadedFilePath));\n if (!proceedWithCommit) {\n this.log('debug', `${compareMsg}: Files match`);\n this.notifySkip({\n name: 'destFileExists',\n message: `Destination file exists with same content (${lastDownloadedFilePath})`,\n existingDestFilePath: lastDownloadedFilePath\n });\n }\n else {\n this.log('debug', `${compareMsg}: Files do not match`);\n }\n }\n if (proceedWithCommit) {\n this.log('debug', `Commit \"${tmpFilePath}\" to \"${destPath}; filesize: ${fs.lstatSync(tmpFilePath).size} bytes`);\n fs.renameSync(tmpFilePath, destPath);\n this.notifyComplete();\n }\n __cleanup();\n resolve();\n }\n });\n\n this.#ffmpegCommand.on('error', (error: any) => {\n if (this.#abortingCallback) {\n this.#abortingCallback();\n }\n else {\n hasError = true;\n this.notifyError(error);\n }\n __cleanup();\n resolve();\n });\n\n this.#abortController.signal.onabort = () => {\n if (this.#ffmpegCommand) {\n this.#ffmpegCommand.kill('SIGKILL');\n }\n else if (this.#abortingCallback) {\n this.#abortingCallback();\n __cleanup();\n resolve();\n }\n };\n\n this.#ffmpegCommand.run();\n }\n catch (error: any) {\n this.notifyError(error);\n __cleanup();\n resolve();\n }\n })\n .finally(() => {\n if (this.#ffmpegCommand) {\n this.#ffmpegCommand.removeAllListeners();\n this.#ffmpegCommand = null;\n }\n this.#abortController = null;\n });\n }\n\n protected async doAbort() {\n return new Promise<void>((resolve) => {\n if (this.#abortController) {\n this.#abortingCallback = () => {\n this.#abortingCallback = null;\n this.notifyAbort();\n resolve();\n };\n this.#abortController.abort();\n }\n else {\n resolve();\n }\n });\n }\n\n protected async doDestroy() {\n this.#destFilePath = '';\n this.#commandLine = null;\n }\n\n protected doGetProgress() {\n return undefined;\n }\n\n get commandLine() {\n return this.#commandLine;\n }\n\n #convertFFmpegProgress(progress: FFmpegProgress): DownloadProgress | null {\n const video = this.srcEntity;\n if (video.duration && this.resolvedDestFilename && this.resolvedDestPath) {\n const length = video.duration;\n const downloaded = this.#timemarkToSeconds(progress.timemark);\n return {\n destFilename: this.resolvedDestFilename,\n destFilePath: this.resolvedDestPath,\n lengthUnit: 'second',\n length: Number(length.toFixed(2)),\n lengthDownloaded: Number(downloaded.toFixed(2)),\n percent: Number((downloaded / length * 100).toFixed(2)),\n sizeDownloaded: Number(progress.targetSize.toFixed(2)),\n speed: Number(progress.currentKbps.toFixed(2))\n };\n }\n return null;\n }\n\n /**\n * https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/blob/master/lib/utils.js\n *\n * Convert a [[hh:]mm:]ss[.xxx] timemark into seconds\n *\n * @param {String} timemark timemark string\n * @return Number\n */\n #timemarkToSeconds(timemark: string) {\n if (typeof timemark === 'number') {\n return timemark;\n }\n\n if (timemark.indexOf(':') === -1 && timemark.indexOf('.') >= 0) {\n return Number(timemark);\n }\n\n const parts = timemark.split(':');\n\n // Add seconds\n let secs = Number(parts.pop());\n\n if (parts.length) {\n // Add minutes\n secs += Number(parts.pop()) * 60;\n }\n\n if (parts.length) {\n // Add hours\n secs += Number(parts.pop()) * 3600;\n }\n\n return secs;\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import Fetcher from '../../utils/Fetcher.js';
2
+ import FilenameResolver from '../../utils/FllenameResolver.js';
3
+ import DownloadTask, { DownloadTaskParams, DownloadProgress } from './DownloadTask.js';
4
+ import { Downloadable } from '../../entities/Downloadable.js';
5
+ import { FileExistsAction } from '../DownloaderOptions.js';
6
+ export interface FetcherDownloadTaskParams<T extends Downloadable> extends DownloadTaskParams {
7
+ fetcher: Fetcher;
8
+ destDir: string;
9
+ destFilenameResolver: FilenameResolver<T>;
10
+ fileExistsAction: FileExistsAction;
11
+ }
12
+ export default class FetcherDownloadTask<T extends Downloadable> extends DownloadTask {
13
+ #private;
14
+ protected name: string;
15
+ constructor(params: FetcherDownloadTaskParams<T>);
16
+ protected doStart(): Promise<void>;
17
+ protected doAbort(): Promise<void>;
18
+ protected doDestroy(): Promise<void>;
19
+ protected doGetProgress(): DownloadProgress | undefined;
20
+ }
21
+ //# sourceMappingURL=FetcherDownloadTask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FetcherDownloadTask.d.ts","sourceRoot":"","sources":["../../../src/downloaders/task/FetcherDownloadTask.ts"],"names":[],"mappings":"AAEA,OAAO,OAAO,MAAM,wBAAwB,CAAC;AAE7C,OAAO,gBAAgB,MAAM,iCAAiC,CAAC;AAC/D,OAAO,YAAY,EAAE,EAAE,kBAAkB,EAAE,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAE/G,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,MAAM,WAAW,yBAAyB,CAAC,CAAC,SAAS,YAAY,CAAE,SAAQ,kBAAkB;IAC3F,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,MAAM,CAAC,OAAO,OAAO,mBAAmB,CAAC,CAAC,SAAS,YAAY,CAAE,SAAQ,YAAY;;IAEnF,SAAS,CAAC,IAAI,SAAyB;gBAW3B,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAkChD,SAAS,CAAC,OAAO;cA0ID,OAAO;cAeP,SAAS;IAIzB,SAAS,CAAC,aAAa;CAmBxB"}