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.
- package/README.md +422 -0
- package/bin/patreon-dl.js +5 -0
- package/dist/cli/CLIOptionValidator.d.ts +9 -0
- package/dist/cli/CLIOptionValidator.d.ts.map +1 -0
- package/dist/cli/CLIOptionValidator.js +85 -0
- package/dist/cli/CLIOptionValidator.js.map +1 -0
- package/dist/cli/CLIOptions.d.ts +20 -0
- package/dist/cli/CLIOptions.d.ts.map +1 -0
- package/dist/cli/CLIOptions.js +75 -0
- package/dist/cli/CLIOptions.js.map +1 -0
- package/dist/cli/CommandLineParser.d.ts +11 -0
- package/dist/cli/CommandLineParser.d.ts.map +1 -0
- package/dist/cli/CommandLineParser.js +212 -0
- package/dist/cli/CommandLineParser.js.map +1 -0
- package/dist/cli/ConfigFileParser.d.ts +9 -0
- package/dist/cli/ConfigFileParser.d.ts.map +1 -0
- package/dist/cli/ConfigFileParser.js +163 -0
- package/dist/cli/ConfigFileParser.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +162 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/downloaders/Bootstrap.d.ts +29 -0
- package/dist/downloaders/Bootstrap.d.ts.map +1 -0
- package/dist/downloaders/Bootstrap.js +51 -0
- package/dist/downloaders/Bootstrap.js.map +1 -0
- package/dist/downloaders/Downloader.d.ts +59 -0
- package/dist/downloaders/Downloader.d.ts.map +1 -0
- package/dist/downloaders/Downloader.js +357 -0
- package/dist/downloaders/Downloader.js.map +1 -0
- package/dist/downloaders/DownloaderEvent.d.ts +47 -0
- package/dist/downloaders/DownloaderEvent.d.ts.map +1 -0
- package/dist/downloaders/DownloaderEvent.js +6 -0
- package/dist/downloaders/DownloaderEvent.js.map +1 -0
- package/dist/downloaders/DownloaderOptions.d.ts +39 -0
- package/dist/downloaders/DownloaderOptions.d.ts.map +1 -0
- package/dist/downloaders/DownloaderOptions.js +69 -0
- package/dist/downloaders/DownloaderOptions.js.map +1 -0
- package/dist/downloaders/PostDownloader.d.ts +8 -0
- package/dist/downloaders/PostDownloader.d.ts.map +1 -0
- package/dist/downloaders/PostDownloader.js +428 -0
- package/dist/downloaders/PostDownloader.js.map +1 -0
- package/dist/downloaders/ProductDownloader.d.ts +8 -0
- package/dist/downloaders/ProductDownloader.d.ts.map +1 -0
- package/dist/downloaders/ProductDownloader.js +171 -0
- package/dist/downloaders/ProductDownloader.js.map +1 -0
- package/dist/downloaders/cache/StatusCache.d.ts +43 -0
- package/dist/downloaders/cache/StatusCache.d.ts.map +1 -0
- package/dist/downloaders/cache/StatusCache.js +206 -0
- package/dist/downloaders/cache/StatusCache.js.map +1 -0
- package/dist/downloaders/index.d.ts +7 -0
- package/dist/downloaders/index.d.ts.map +1 -0
- package/dist/downloaders/index.js +6 -0
- package/dist/downloaders/index.js.map +1 -0
- package/dist/downloaders/task/DownloadTask.d.ts +89 -0
- package/dist/downloaders/task/DownloadTask.d.ts.map +1 -0
- package/dist/downloaders/task/DownloadTask.js +240 -0
- package/dist/downloaders/task/DownloadTask.js.map +1 -0
- package/dist/downloaders/task/DownloadTaskBatch.d.ts +45 -0
- package/dist/downloaders/task/DownloadTaskBatch.d.ts.map +1 -0
- package/dist/downloaders/task/DownloadTaskBatch.js +195 -0
- package/dist/downloaders/task/DownloadTaskBatch.js.map +1 -0
- package/dist/downloaders/task/DownloadTaskBatchEvent.d.ts +32 -0
- package/dist/downloaders/task/DownloadTaskBatchEvent.d.ts.map +1 -0
- package/dist/downloaders/task/DownloadTaskBatchEvent.js +2 -0
- package/dist/downloaders/task/DownloadTaskBatchEvent.js.map +1 -0
- package/dist/downloaders/task/DownloadTaskFactory.d.ts +20 -0
- package/dist/downloaders/task/DownloadTaskFactory.d.ts.map +1 -0
- package/dist/downloaders/task/DownloadTaskFactory.js +177 -0
- package/dist/downloaders/task/DownloadTaskFactory.js.map +1 -0
- package/dist/downloaders/task/FFmpegDownloadTask.d.ts +27 -0
- package/dist/downloaders/task/FFmpegDownloadTask.d.ts.map +1 -0
- package/dist/downloaders/task/FFmpegDownloadTask.js +206 -0
- package/dist/downloaders/task/FFmpegDownloadTask.js.map +1 -0
- package/dist/downloaders/task/FetcherDownloadTask.d.ts +21 -0
- package/dist/downloaders/task/FetcherDownloadTask.d.ts.map +1 -0
- package/dist/downloaders/task/FetcherDownloadTask.js +213 -0
- package/dist/downloaders/task/FetcherDownloadTask.js.map +1 -0
- package/dist/downloaders/task/index.d.ts +4 -0
- package/dist/downloaders/task/index.d.ts.map +1 -0
- package/dist/downloaders/task/index.js +3 -0
- package/dist/downloaders/task/index.js.map +1 -0
- package/dist/downloaders/templates/CampaignInfo.d.ts +3 -0
- package/dist/downloaders/templates/CampaignInfo.d.ts.map +1 -0
- package/dist/downloaders/templates/CampaignInfo.js +58 -0
- package/dist/downloaders/templates/CampaignInfo.js.map +1 -0
- package/dist/downloaders/templates/PostInfo.d.ts +4 -0
- package/dist/downloaders/templates/PostInfo.d.ts.map +1 -0
- package/dist/downloaders/templates/PostInfo.js +45 -0
- package/dist/downloaders/templates/PostInfo.js.map +1 -0
- package/dist/downloaders/templates/ProductInfo.d.ts +3 -0
- package/dist/downloaders/templates/ProductInfo.d.ts.map +1 -0
- package/dist/downloaders/templates/ProductInfo.js +20 -0
- package/dist/downloaders/templates/ProductInfo.js.map +1 -0
- package/dist/entities/Attachment.d.ts +7 -0
- package/dist/entities/Attachment.d.ts.map +1 -0
- package/dist/entities/Attachment.js +2 -0
- package/dist/entities/Attachment.js.map +1 -0
- package/dist/entities/Campaign.d.ts +19 -0
- package/dist/entities/Campaign.d.ts.map +1 -0
- package/dist/entities/Campaign.js +2 -0
- package/dist/entities/Campaign.js.map +1 -0
- package/dist/entities/Downloadable.d.ts +6 -0
- package/dist/entities/Downloadable.d.ts.map +1 -0
- package/dist/entities/Downloadable.js +5 -0
- package/dist/entities/Downloadable.js.map +1 -0
- package/dist/entities/MediaItem.d.ts +95 -0
- package/dist/entities/MediaItem.d.ts.map +1 -0
- package/dist/entities/MediaItem.js +2 -0
- package/dist/entities/MediaItem.js.map +1 -0
- package/dist/entities/Post.d.ts +87 -0
- package/dist/entities/Post.d.ts.map +1 -0
- package/dist/entities/Post.js +2 -0
- package/dist/entities/Post.js.map +1 -0
- package/dist/entities/Product.d.ts +17 -0
- package/dist/entities/Product.d.ts.map +1 -0
- package/dist/entities/Product.js +2 -0
- package/dist/entities/Product.js.map +1 -0
- package/dist/entities/Reward.d.ts +14 -0
- package/dist/entities/Reward.d.ts.map +1 -0
- package/dist/entities/Reward.js +2 -0
- package/dist/entities/Reward.js.map +1 -0
- package/dist/entities/User.d.ts +15 -0
- package/dist/entities/User.d.ts.map +1 -0
- package/dist/entities/User.js +2 -0
- package/dist/entities/User.js.map +1 -0
- package/dist/entities/index.d.ts +9 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +6 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/PageParser.d.ts +6 -0
- package/dist/parsers/PageParser.d.ts.map +1 -0
- package/dist/parsers/PageParser.js +23 -0
- package/dist/parsers/PageParser.js.map +1 -0
- package/dist/parsers/Parser.d.ts +43 -0
- package/dist/parsers/Parser.d.ts.map +1 -0
- package/dist/parsers/Parser.js +439 -0
- package/dist/parsers/Parser.js.map +1 -0
- package/dist/parsers/PostParser.d.ts +7 -0
- package/dist/parsers/PostParser.d.ts.map +1 -0
- package/dist/parsers/PostParser.js +259 -0
- package/dist/parsers/PostParser.js.map +1 -0
- package/dist/parsers/ProductParser.d.ts +7 -0
- package/dist/parsers/ProductParser.d.ts.map +1 -0
- package/dist/parsers/ProductParser.js +70 -0
- package/dist/parsers/ProductParser.js.map +1 -0
- package/dist/utils/AttachmentFilenameResolver.d.ts +9 -0
- package/dist/utils/AttachmentFilenameResolver.d.ts.map +1 -0
- package/dist/utils/AttachmentFilenameResolver.js +73 -0
- package/dist/utils/AttachmentFilenameResolver.js.map +1 -0
- package/dist/utils/FSHelper.d.ts +57 -0
- package/dist/utils/FSHelper.d.ts.map +1 -0
- package/dist/utils/FSHelper.js +214 -0
- package/dist/utils/FSHelper.js.map +1 -0
- package/dist/utils/Fetcher.d.ts +45 -0
- package/dist/utils/Fetcher.d.ts.map +1 -0
- package/dist/utils/Fetcher.js +192 -0
- package/dist/utils/Fetcher.js.map +1 -0
- package/dist/utils/FetcherProgressMonitor.d.ts +18 -0
- package/dist/utils/FetcherProgressMonitor.d.ts.map +1 -0
- package/dist/utils/FetcherProgressMonitor.js +56 -0
- package/dist/utils/FetcherProgressMonitor.js.map +1 -0
- package/dist/utils/FilenameFormatHelper.d.ts +44 -0
- package/dist/utils/FilenameFormatHelper.d.ts.map +1 -0
- package/dist/utils/FilenameFormatHelper.js +98 -0
- package/dist/utils/FilenameFormatHelper.js.map +1 -0
- package/dist/utils/FllenameResolver.d.ts +20 -0
- package/dist/utils/FllenameResolver.d.ts.map +1 -0
- package/dist/utils/FllenameResolver.js +55 -0
- package/dist/utils/FllenameResolver.js.map +1 -0
- package/dist/utils/Formatter.d.ts +21 -0
- package/dist/utils/Formatter.d.ts.map +1 -0
- package/dist/utils/Formatter.js +112 -0
- package/dist/utils/Formatter.js.map +1 -0
- package/dist/utils/MediaFilenameResolver.d.ts +9 -0
- package/dist/utils/MediaFilenameResolver.d.ts.map +1 -0
- package/dist/utils/MediaFilenameResolver.js +90 -0
- package/dist/utils/MediaFilenameResolver.js.map +1 -0
- package/dist/utils/Misc.d.ts +14 -0
- package/dist/utils/Misc.d.ts.map +1 -0
- package/dist/utils/Misc.js +4 -0
- package/dist/utils/Misc.js.map +1 -0
- package/dist/utils/ObjectHelper.d.ts +4 -0
- package/dist/utils/ObjectHelper.d.ts.map +1 -0
- package/dist/utils/ObjectHelper.js +30 -0
- package/dist/utils/ObjectHelper.js.map +1 -0
- package/dist/utils/PackageInfo.d.ts +10 -0
- package/dist/utils/PackageInfo.d.ts.map +1 -0
- package/dist/utils/PackageInfo.js +33 -0
- package/dist/utils/PackageInfo.js.map +1 -0
- package/dist/utils/URLHelper.d.ts +40 -0
- package/dist/utils/URLHelper.d.ts.map +1 -0
- package/dist/utils/URLHelper.js +192 -0
- package/dist/utils/URLHelper.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logging/ChainLogger.d.ts +11 -0
- package/dist/utils/logging/ChainLogger.d.ts.map +1 -0
- package/dist/utils/logging/ChainLogger.js +50 -0
- package/dist/utils/logging/ChainLogger.js.map +1 -0
- package/dist/utils/logging/ConsoleLogger.d.ts +31 -0
- package/dist/utils/logging/ConsoleLogger.d.ts.map +1 -0
- package/dist/utils/logging/ConsoleLogger.js +126 -0
- package/dist/utils/logging/ConsoleLogger.js.map +1 -0
- package/dist/utils/logging/FileLogger.d.ts +26 -0
- package/dist/utils/logging/FileLogger.d.ts.map +1 -0
- package/dist/utils/logging/FileLogger.js +147 -0
- package/dist/utils/logging/FileLogger.js.map +1 -0
- package/dist/utils/logging/Logger.d.ts +12 -0
- package/dist/utils/logging/Logger.d.ts.map +1 -0
- package/dist/utils/logging/Logger.js +15 -0
- package/dist/utils/logging/Logger.js.map +1 -0
- package/dist/utils/logging/index.d.ts +7 -0
- package/dist/utils/logging/index.d.ts.map +1 -0
- package/dist/utils/logging/index.js +7 -0
- package/dist/utils/logging/index.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,357 @@
|
|
|
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 __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
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");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Downloader_instances, _Downloader_hasEmittedEndEventOnAbort, _Downloader_validateOptions;
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
import deepFreeze from 'deep-freeze';
|
|
16
|
+
import Fetcher from '../utils/Fetcher.js';
|
|
17
|
+
import Bootstrap from './Bootstrap.js';
|
|
18
|
+
import { getDownloaderInit } from './DownloaderOptions.js';
|
|
19
|
+
import { commonLog } from '../utils/logging/Logger.js';
|
|
20
|
+
import FSHelper from '../utils/FSHelper.js';
|
|
21
|
+
import DownloadTaskBatch from './task/DownloadTaskBatch.js';
|
|
22
|
+
import FFmpegDownloadTask from './task/FFmpegDownloadTask.js';
|
|
23
|
+
import DownloadTaskFactory from './task/DownloadTaskFactory.js';
|
|
24
|
+
import FilenameFormatHelper from '../utils/FilenameFormatHelper.js';
|
|
25
|
+
import { generateCampaignSummary } from './templates/CampaignInfo.js';
|
|
26
|
+
import path from 'path';
|
|
27
|
+
import URLHelper from '../utils/URLHelper.js';
|
|
28
|
+
import { AbortError } from 'node-fetch';
|
|
29
|
+
import ffmpeg from 'fluent-ffmpeg';
|
|
30
|
+
export default class Downloader extends EventEmitter {
|
|
31
|
+
constructor(bootstrap, options) {
|
|
32
|
+
super();
|
|
33
|
+
_Downloader_instances.add(this);
|
|
34
|
+
_Downloader_hasEmittedEndEventOnAbort.set(this, void 0);
|
|
35
|
+
__classPrivateFieldGet(this, _Downloader_instances, "m", _Downloader_validateOptions).call(this, options);
|
|
36
|
+
this.config = deepFreeze({
|
|
37
|
+
...bootstrap,
|
|
38
|
+
...getDownloaderInit(options)
|
|
39
|
+
});
|
|
40
|
+
if (this.config.pathToFFmpeg) {
|
|
41
|
+
ffmpeg.setFfmpegPath(this.config.pathToFFmpeg);
|
|
42
|
+
}
|
|
43
|
+
this.fetcher = new Fetcher(options?.cookie, options?.logger);
|
|
44
|
+
this.logger = options?.logger;
|
|
45
|
+
__classPrivateFieldSet(this, _Downloader_hasEmittedEndEventOnAbort, false, "f");
|
|
46
|
+
}
|
|
47
|
+
createDownloadTaskBatch(name, ...createTasks) {
|
|
48
|
+
const __getDownloadIdString = (task, batch) => {
|
|
49
|
+
let result = `#${batch.id}.${task.id}`;
|
|
50
|
+
if (task.retryCount > 0) {
|
|
51
|
+
result += `-r${task.retryCount}`;
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
const batch = new DownloadTaskBatch({
|
|
56
|
+
name,
|
|
57
|
+
fetcher: this.fetcher,
|
|
58
|
+
limiter: this.config.request
|
|
59
|
+
});
|
|
60
|
+
batch.on('taskStart', ({ task }) => {
|
|
61
|
+
const retryOrBeginStr = task.retryCount > 0 ? 'retry' : 'begin';
|
|
62
|
+
this.log('info', `Download ${retryOrBeginStr} (${__getDownloadIdString(task, batch)}): [type: ${task.srcEntity.type}; ID: #${task.srcEntity.id}] -> ${task.resolvedDestFilename}`);
|
|
63
|
+
if (task instanceof FFmpegDownloadTask) {
|
|
64
|
+
const retryOrBeginStr = task.retryCount > 0 ? 'Retry' : 'Begin';
|
|
65
|
+
this.log('info', `${retryOrBeginStr} downloading through FFmpeg (${__getDownloadIdString(task, batch)}): ${task.commandLine}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
batch.on('taskComplete', ({ task }) => {
|
|
69
|
+
this.log('info', `Download complete (${__getDownloadIdString(task, batch)}): "${task.resolvedDestPath}"`);
|
|
70
|
+
});
|
|
71
|
+
batch.on('taskError', ({ error, willRetry }) => {
|
|
72
|
+
const { task, cause } = error;
|
|
73
|
+
const retryStr = willRetry ? '- will retry' : '';
|
|
74
|
+
this.log('error', `Download error (${__getDownloadIdString(task, batch)}):`, cause, `(${task.src})`, retryStr);
|
|
75
|
+
});
|
|
76
|
+
batch.on('taskAbort', ({ task }) => {
|
|
77
|
+
this.log('warn', `Download aborted (${__getDownloadIdString(task, batch)})`);
|
|
78
|
+
});
|
|
79
|
+
batch.on('taskSkip', ({ task, reason }) => {
|
|
80
|
+
this.log('warn', `Download skipped (${__getDownloadIdString(task, batch)}): ${reason.message}`);
|
|
81
|
+
});
|
|
82
|
+
batch.on('taskSpawn', ({ origin, spawn }) => {
|
|
83
|
+
this.log('info', `Download spawned: #${batch.id}.${origin.id} -> #${batch.id}.${spawn.id}`);
|
|
84
|
+
});
|
|
85
|
+
batch.on('complete', () => {
|
|
86
|
+
const total = batch.getTasks().length;
|
|
87
|
+
const completed = batch.getTasks('completed').length;
|
|
88
|
+
const error = batch.getTasks('error').length;
|
|
89
|
+
const aborted = batch.getTasks('aborted').length;
|
|
90
|
+
const skipped = batch.getTasks('skipped').length;
|
|
91
|
+
const counts = [
|
|
92
|
+
`${total} downloads`,
|
|
93
|
+
`${completed} completed`,
|
|
94
|
+
`${error} errors`,
|
|
95
|
+
`${skipped} skipped`,
|
|
96
|
+
`${aborted} aborted`
|
|
97
|
+
].join('; ');
|
|
98
|
+
this.log('info', `Download batch complete (#${batch.id}): ${counts}`);
|
|
99
|
+
});
|
|
100
|
+
return this.addToDownloadTaskBatch(batch, ...createTasks);
|
|
101
|
+
}
|
|
102
|
+
addToDownloadTaskBatch(batch, ...createTasks) {
|
|
103
|
+
let failedCreateTaskCount = 0;
|
|
104
|
+
for (const task of createTasks) {
|
|
105
|
+
if (!task) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const { target, targetName, destDir } = task;
|
|
109
|
+
this.log('info', `Create download tasks for ${targetName}`);
|
|
110
|
+
if (task.target.length === 0) {
|
|
111
|
+
this.log('warn', `No items in ${targetName}`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
let createdCount = 0;
|
|
115
|
+
for (const tt of target) {
|
|
116
|
+
try {
|
|
117
|
+
const tasks = DownloadTaskFactory.createFromDownloadable({
|
|
118
|
+
item: tt,
|
|
119
|
+
destDir,
|
|
120
|
+
fetcher: this.fetcher,
|
|
121
|
+
destFilenameFormat: this.config.filenameFormat.media,
|
|
122
|
+
fileExistsAction: task.fileExistsAction,
|
|
123
|
+
maxRetries: this.config.request.maxRetries,
|
|
124
|
+
downloadAllVariants: this.config.include.allMediaVariants,
|
|
125
|
+
logger: this.logger
|
|
126
|
+
});
|
|
127
|
+
batch.addTasks(tasks);
|
|
128
|
+
createdCount += tasks.length;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
this.log('error', `Failed to create download task for item #${tt.id} in ${targetName}:`, error);
|
|
132
|
+
failedCreateTaskCount++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (createdCount > 0) {
|
|
136
|
+
FSHelper.createDir(destDir);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (failedCreateTaskCount > 0) {
|
|
140
|
+
this.log('warn', `${failedCreateTaskCount} items could not be processed for downloading`);
|
|
141
|
+
}
|
|
142
|
+
return batch;
|
|
143
|
+
}
|
|
144
|
+
static async getInstance(url, options) {
|
|
145
|
+
const bootstrap = Bootstrap.getDownloaderBootstrapDataByURL(url);
|
|
146
|
+
if (!bootstrap) {
|
|
147
|
+
throw Error('Could not determine downloader type from URL');
|
|
148
|
+
}
|
|
149
|
+
switch (bootstrap.type) {
|
|
150
|
+
case 'product':
|
|
151
|
+
const ProductDownloader = (await import('./ProductDownloader.js')).default;
|
|
152
|
+
return new ProductDownloader(bootstrap, options);
|
|
153
|
+
case 'post':
|
|
154
|
+
const PostDownloader = (await import('./PostDownloader.js')).default;
|
|
155
|
+
return new PostDownloader(bootstrap, options);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
saveCampaignInfo(campaign, signal) {
|
|
159
|
+
return new Promise(async (resolve) => {
|
|
160
|
+
if (!this.config.include.campaignInfo) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (this.checkAbortSignal(signal, resolve)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
let batch = null;
|
|
167
|
+
const abortHandler = async () => {
|
|
168
|
+
if (batch) {
|
|
169
|
+
await batch.abort();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (signal) {
|
|
173
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
174
|
+
}
|
|
175
|
+
if (!campaign) {
|
|
176
|
+
this.log('warn', 'Skipped saving campaign info: target unavailable');
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
this.log('info', `Save campaign info #${campaign.id}`);
|
|
180
|
+
this.emit('targetBegin', { target: campaign });
|
|
181
|
+
this.emit('phaseBegin', { target: campaign, phase: 'saveInfo' });
|
|
182
|
+
// Step 1: create campaign directories
|
|
183
|
+
const campaignDirs = FSHelper.getCampaignDirs(campaign, this.config);
|
|
184
|
+
this.log('debug', 'Campaign directories: ', campaignDirs);
|
|
185
|
+
FSHelper.createDir(campaignDirs.root);
|
|
186
|
+
FSHelper.createDir(campaignDirs.info);
|
|
187
|
+
// Step 2: save summary and raw json
|
|
188
|
+
const summary = generateCampaignSummary(campaign);
|
|
189
|
+
const summaryFile = path.resolve(campaignDirs.info, 'info.txt');
|
|
190
|
+
const saveSummaryResult = await FSHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);
|
|
191
|
+
this.logWriteTextFileResult(saveSummaryResult, campaign, 'campaign summary');
|
|
192
|
+
// Campaign / creator raw data might not be complete. Fetch directly from API.
|
|
193
|
+
// Strictly speaking, we should check for 'error' in results, but since it's not going to be fatal we'll just skip it.
|
|
194
|
+
const { json: fetchedCampaignAPIData } = await this.fetchCampaign(campaign.id, signal);
|
|
195
|
+
const { json: fetchedCreatorAPIData } = campaign.creator ? await this.fetchUser(campaign.creator.id, signal) : { json: null };
|
|
196
|
+
if (this.checkAbortSignal(signal, resolve)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const campaignRawFile = path.resolve(campaignDirs.info, 'campaign-api.json');
|
|
200
|
+
const saveCampaignRawResult = await FSHelper.writeTextFile(campaignRawFile, fetchedCampaignAPIData || campaign.raw, this.config.fileExistsAction.infoAPI);
|
|
201
|
+
this.logWriteTextFileResult(saveCampaignRawResult, campaign, 'campaign API data');
|
|
202
|
+
if (campaign.creator) {
|
|
203
|
+
const creatorRawFile = path.resolve(campaignDirs.info, 'creator-api.json');
|
|
204
|
+
const saveCreatorRawResult = await FSHelper.writeTextFile(creatorRawFile, fetchedCreatorAPIData || campaign.creator.raw, this.config.fileExistsAction.infoAPI);
|
|
205
|
+
this.logWriteTextFileResult(saveCreatorRawResult, campaign.creator, 'creator API data');
|
|
206
|
+
}
|
|
207
|
+
this.emit('phaseEnd', { target: campaign, phase: 'saveInfo' });
|
|
208
|
+
// Step 3: download campaign media items
|
|
209
|
+
this.emit('phaseBegin', { target: campaign, phase: 'saveMedia' });
|
|
210
|
+
const campaignMedia = [
|
|
211
|
+
campaign.avatarImage,
|
|
212
|
+
campaign.coverPhoto
|
|
213
|
+
];
|
|
214
|
+
if (campaign.creator) {
|
|
215
|
+
campaignMedia.push(campaign.creator.image, campaign.creator.thumbnail);
|
|
216
|
+
}
|
|
217
|
+
for (const reward of campaign.rewards) {
|
|
218
|
+
if (reward.image) {
|
|
219
|
+
campaignMedia.push(reward.image);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
batch = this.createDownloadTaskBatch(`Campaign #${campaign.id} (${campaign.name})`, {
|
|
223
|
+
target: campaignMedia,
|
|
224
|
+
targetName: `campaign #${campaign.id} -> images`,
|
|
225
|
+
destDir: campaignDirs.info,
|
|
226
|
+
fileExistsAction: this.config.fileExistsAction.info
|
|
227
|
+
});
|
|
228
|
+
this.emit('phaseBegin', { target: campaign, phase: 'batchDownload', batch });
|
|
229
|
+
await batch.start();
|
|
230
|
+
await batch.destroy();
|
|
231
|
+
this.emit('phaseEnd', { target: campaign, phase: 'batchDownload' });
|
|
232
|
+
this.emit('phaseEnd', { target: campaign, phase: 'saveMedia' });
|
|
233
|
+
if (signal) {
|
|
234
|
+
signal.removeEventListener('abort', abortHandler);
|
|
235
|
+
}
|
|
236
|
+
if (this.checkAbortSignal(signal, resolve)) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Done
|
|
240
|
+
this.log('info', 'Done saving campaign info');
|
|
241
|
+
this.emit('targetEnd', { target: campaign, isSkipped: false });
|
|
242
|
+
resolve();
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
log(level, ...msg) {
|
|
246
|
+
commonLog(this.logger, level, this.name, ...msg);
|
|
247
|
+
}
|
|
248
|
+
getConfig() {
|
|
249
|
+
return this.config;
|
|
250
|
+
}
|
|
251
|
+
checkAbortSignal(signal, resolve) {
|
|
252
|
+
if (signal && signal.aborted) {
|
|
253
|
+
if (!__classPrivateFieldGet(this, _Downloader_hasEmittedEndEventOnAbort, "f")) {
|
|
254
|
+
this.emit('end', { aborted: true });
|
|
255
|
+
__classPrivateFieldSet(this, _Downloader_hasEmittedEndEventOnAbort, true, "f");
|
|
256
|
+
}
|
|
257
|
+
resolve();
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
logWriteTextFileResult(result, target, targetName) {
|
|
263
|
+
switch (result.status) {
|
|
264
|
+
case 'completed':
|
|
265
|
+
this.log('info', `Saved ${targetName} to "${result.filePath}"`);
|
|
266
|
+
break;
|
|
267
|
+
case 'skipped':
|
|
268
|
+
this.log('warn', `Skipped saving ${targetName} #${target.id}: ${result.message}`);
|
|
269
|
+
break;
|
|
270
|
+
case 'error':
|
|
271
|
+
this.log('error', `Error saving ${targetName} #${target.id} to "${result.filePath}":`, result.error);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async commonFetchAPI(url, signal) {
|
|
275
|
+
let json, requestAPIError;
|
|
276
|
+
try {
|
|
277
|
+
json = await this.fetcher.get({ url, type: 'json', maxRetries: this.config.request.maxRetries, signal });
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (error instanceof AbortError) {
|
|
281
|
+
this.log('warn', 'API request aborted');
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
this.log('error', `Error requesting API URL "${url}": `, error);
|
|
285
|
+
requestAPIError = error;
|
|
286
|
+
}
|
|
287
|
+
json = null;
|
|
288
|
+
}
|
|
289
|
+
return { json, error: requestAPIError };
|
|
290
|
+
}
|
|
291
|
+
fetchCampaign(campaignId, signal) {
|
|
292
|
+
const url = URLHelper.constructCampaignAPIURL(campaignId);
|
|
293
|
+
this.log('debug', `Fetch campaign data from API URL "${url}"`);
|
|
294
|
+
return this.commonFetchAPI(url, signal);
|
|
295
|
+
}
|
|
296
|
+
fetchUser(userId, signal) {
|
|
297
|
+
const url = URLHelper.constructUserAPIURL(userId);
|
|
298
|
+
this.log('debug', `Fetch user data from API URL "${url}"`);
|
|
299
|
+
return this.commonFetchAPI(url, signal);
|
|
300
|
+
}
|
|
301
|
+
on(event, listener) {
|
|
302
|
+
return super.on(event, listener);
|
|
303
|
+
}
|
|
304
|
+
once(event, listener) {
|
|
305
|
+
return super.on(event, listener);
|
|
306
|
+
}
|
|
307
|
+
off(event, listener) {
|
|
308
|
+
return super.off(event, listener);
|
|
309
|
+
}
|
|
310
|
+
emit(event, ...args) {
|
|
311
|
+
return super.emit(event, ...args);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
_Downloader_hasEmittedEndEventOnAbort = new WeakMap(), _Downloader_instances = new WeakSet(), _Downloader_validateOptions = function _Downloader_validateOptions(options) {
|
|
315
|
+
if (!options) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
// Check FFmpeg path exists
|
|
319
|
+
if (options.pathToFFmpeg) {
|
|
320
|
+
if (!fs.existsSync(options.pathToFFmpeg)) {
|
|
321
|
+
throw Error(`Path to FFmpeg executable "${options.pathToFFmpeg}" does not exist`);
|
|
322
|
+
}
|
|
323
|
+
else if (!fs.lstatSync(options.pathToFFmpeg).isFile()) {
|
|
324
|
+
throw Error(`Path to FFmpeg executable "${options.pathToFFmpeg}" does not point to a file`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Check outDir is a directory
|
|
328
|
+
if (options.outDir) {
|
|
329
|
+
if (fs.existsSync(options.outDir) && !fs.lstatSync(options.outDir).isDirectory()) {
|
|
330
|
+
throw Error(`"${options.outDir}" is not a directory`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Check formats are valid
|
|
334
|
+
const campaignDirNameFormat = options.dirNameFormat?.campaign;
|
|
335
|
+
if (campaignDirNameFormat) {
|
|
336
|
+
const validate = FilenameFormatHelper.validateCampaignDirNameFormat(campaignDirNameFormat);
|
|
337
|
+
if (!validate.validateOK) {
|
|
338
|
+
throw Error(`Campaign directory name format '${campaignDirNameFormat}' is invalid (matched against ${validate.regex})`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const contentDirNameFormat = options.dirNameFormat?.content;
|
|
342
|
+
if (contentDirNameFormat) {
|
|
343
|
+
const validate = FilenameFormatHelper.validateContentDirNameFormat(contentDirNameFormat);
|
|
344
|
+
if (!validate.validateOK) {
|
|
345
|
+
throw Error(`Content directory name format '${contentDirNameFormat}' is invalid (matched against ${validate.regex})`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const mediaFilenameFormat = options.filenameFormat?.media;
|
|
349
|
+
if (mediaFilenameFormat) {
|
|
350
|
+
const validate = FilenameFormatHelper.validateMediaFilenameFormat(mediaFilenameFormat);
|
|
351
|
+
if (!validate.validateOK) {
|
|
352
|
+
throw Error(`Media filename format '${mediaFilenameFormat}' is invalid (matched against ${validate.regex})`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return true;
|
|
356
|
+
};
|
|
357
|
+
//# sourceMappingURL=Downloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Downloader.js","sourceRoot":"","sources":["../../src/downloaders/Downloader.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport { EventEmitter } from 'events';\nimport deepFreeze from 'deep-freeze';\nimport Fetcher from '../utils/Fetcher.js';\nimport Bootstrap, { DownloaderBootstrapData, DownloaderType } from './Bootstrap.js';\nimport { DownloaderInit, DownloaderOptions, FileExistsAction, getDownloaderInit } from './DownloaderOptions.js';\nimport { DownloaderEvent, DownloaderEventPayloadOf } from './DownloaderEvent.js';\nimport Logger, { LogLevel, commonLog } from '../utils/logging/Logger.js';\nimport { Campaign } from '../entities/Campaign.js';\nimport FSHelper, { WriteTextFileResult } from '../utils/FSHelper.js';\nimport DownloadTaskBatch from './task/DownloadTaskBatch.js';\nimport { IDownloadTask } from './task/DownloadTask.js';\nimport FFmpegDownloadTask from './task/FFmpegDownloadTask.js';\nimport DownloadTaskFactory from './task/DownloadTaskFactory.js';\nimport FilenameFormatHelper from '../utils/FilenameFormatHelper.js';\nimport { Downloadable } from '../entities/Downloadable.js';\nimport { generateCampaignSummary } from './templates/CampaignInfo.js';\nimport path from 'path';\nimport URLHelper from '../utils/URLHelper.js';\nimport { AbortError } from 'node-fetch';\nimport ffmpeg from 'fluent-ffmpeg';\n\nexport type DownloaderConfig<T extends DownloaderType> =\n DownloaderInit &\n Omit<DownloaderBootstrapData<T>, 'type'>;\n\nexport interface DownloaderStartParams {\n signal?: AbortSignal;\n}\n\ninterface CreateDownloadTaskParams {\n target: Downloadable[];\n targetName: string;\n destDir: string;\n fileExistsAction: FileExistsAction;\n}\n\nexport default abstract class Downloader<T extends DownloaderType> extends EventEmitter {\n\n abstract name: string;\n\n protected fetcher: Fetcher;\n protected config: deepFreeze.DeepReadonly<DownloaderConfig<T>>;\n protected logger?: Logger | null;\n\n #hasEmittedEndEventOnAbort: boolean;\n\n constructor(bootstrap: DownloaderBootstrapData<T>, options?: DownloaderOptions) {\n super();\n this.#validateOptions(options);\n this.config = deepFreeze({\n ...bootstrap,\n ...getDownloaderInit(options)\n });\n if (this.config.pathToFFmpeg) {\n ffmpeg.setFfmpegPath(this.config.pathToFFmpeg);\n }\n this.fetcher = new Fetcher(options?.cookie, options?.logger);\n this.logger = options?.logger;\n this.#hasEmittedEndEventOnAbort = false;\n }\n\n protected createDownloadTaskBatch(name: string, ...createTasks: Array<CreateDownloadTaskParams | null>): DownloadTaskBatch {\n\n const __getDownloadIdString = (task: IDownloadTask, batch: DownloadTaskBatch) => {\n let result = `#${batch.id}.${task.id}`;\n if (task.retryCount > 0) {\n result += `-r${task.retryCount}`;\n }\n return result;\n };\n\n const batch = new DownloadTaskBatch({\n name,\n fetcher: this.fetcher,\n limiter: this.config.request\n });\n\n batch.on('taskStart', ({task}) => {\n const retryOrBeginStr = task.retryCount > 0 ? 'retry' : 'begin';\n this.log('info', `Download ${retryOrBeginStr} (${__getDownloadIdString(task, batch)}): [type: ${task.srcEntity.type}; ID: #${task.srcEntity.id}] -> ${task.resolvedDestFilename}`);\n if (task instanceof FFmpegDownloadTask) {\n const retryOrBeginStr = task.retryCount > 0 ? 'Retry' : 'Begin';\n this.log('info', `${retryOrBeginStr} downloading through FFmpeg (${__getDownloadIdString(task, batch)}): ${task.commandLine}`);\n }\n });\n\n batch.on('taskComplete', ({task}) => {\n this.log('info', `Download complete (${__getDownloadIdString(task, batch)}): \"${task.resolvedDestPath}\"`);\n });\n\n batch.on('taskError', ({error, willRetry}) => {\n const { task, cause } = error;\n const retryStr = willRetry ? '- will retry' : '';\n this.log('error', `Download error (${__getDownloadIdString(task, batch)}):`, cause, `(${task.src})`, retryStr);\n });\n\n batch.on('taskAbort', ({task}) => {\n this.log('warn', `Download aborted (${__getDownloadIdString(task, batch)})`);\n });\n\n batch.on('taskSkip', ({task, reason}) => {\n this.log('warn', `Download skipped (${__getDownloadIdString(task, batch)}): ${reason.message}`);\n });\n\n batch.on('taskSpawn', ({origin, spawn}) => {\n this.log('info', `Download spawned: #${batch.id}.${origin.id} -> #${batch.id}.${spawn.id}`);\n });\n\n batch.on('complete', () => {\n const total = batch.getTasks().length;\n const completed = batch.getTasks('completed').length;\n const error = batch.getTasks('error').length;\n const aborted = batch.getTasks('aborted').length;\n const skipped = batch.getTasks('skipped').length;\n const counts = [\n `${total} downloads`,\n `${completed} completed`,\n `${error} errors`,\n `${skipped} skipped`,\n `${aborted} aborted`\n ].join('; ');\n this.log('info', `Download batch complete (#${batch.id}): ${counts}`);\n });\n\n return this.addToDownloadTaskBatch(batch, ...createTasks);\n }\n\n protected addToDownloadTaskBatch(batch: DownloadTaskBatch, ...createTasks: Array<CreateDownloadTaskParams | null>) {\n let failedCreateTaskCount = 0;\n for (const task of createTasks) {\n if (!task) {\n continue;\n }\n const { target, targetName, destDir } = task;\n this.log('info', `Create download tasks for ${targetName}`);\n if (task.target.length === 0) {\n this.log('warn', `No items in ${targetName}`);\n continue;\n }\n let createdCount = 0;\n for (const tt of target) {\n try {\n const tasks = DownloadTaskFactory.createFromDownloadable({\n item: tt,\n destDir,\n fetcher: this.fetcher,\n destFilenameFormat: this.config.filenameFormat.media,\n fileExistsAction: task.fileExistsAction,\n maxRetries: this.config.request.maxRetries,\n downloadAllVariants: this.config.include.allMediaVariants,\n logger: this.logger\n });\n batch.addTasks(tasks);\n createdCount += tasks.length;\n }\n catch (error) {\n this.log('error', `Failed to create download task for item #${tt.id} in ${targetName}:`, error);\n failedCreateTaskCount++;\n }\n }\n if (createdCount > 0) {\n FSHelper.createDir(destDir);\n }\n }\n if (failedCreateTaskCount > 0) {\n this.log('warn', `${failedCreateTaskCount} items could not be processed for downloading`);\n }\n return batch;\n }\n\n abstract start(params: DownloaderStartParams): Promise<void>;\n\n static async getInstance(url: string, options?: DownloaderOptions) {\n const bootstrap = Bootstrap.getDownloaderBootstrapDataByURL(url);\n if (!bootstrap) {\n throw Error('Could not determine downloader type from URL');\n }\n switch (bootstrap.type) {\n case 'product':\n const ProductDownloader = (await import('./ProductDownloader.js')).default;\n return new ProductDownloader(bootstrap, options);\n case 'post':\n const PostDownloader = (await import('./PostDownloader.js')).default;\n return new PostDownloader(bootstrap, options);\n }\n }\n\n #validateOptions(options?: DownloaderOptions) {\n if (!options) {\n return true;\n }\n\n // Check FFmpeg path exists\n if (options.pathToFFmpeg) {\n if (!fs.existsSync(options.pathToFFmpeg)) {\n throw Error(`Path to FFmpeg executable \"${options.pathToFFmpeg}\" does not exist`);\n }\n else if (!fs.lstatSync(options.pathToFFmpeg).isFile()) {\n throw Error(`Path to FFmpeg executable \"${options.pathToFFmpeg}\" does not point to a file`);\n }\n }\n\n // Check outDir is a directory\n if (options.outDir) {\n if (fs.existsSync(options.outDir) && !fs.lstatSync(options.outDir).isDirectory()) {\n throw Error(`\"${options.outDir}\" is not a directory`);\n }\n }\n\n // Check formats are valid\n const campaignDirNameFormat = options.dirNameFormat?.campaign;\n if (campaignDirNameFormat) {\n const validate = FilenameFormatHelper.validateCampaignDirNameFormat(campaignDirNameFormat);\n if (!validate.validateOK) {\n throw Error(`Campaign directory name format '${campaignDirNameFormat}' is invalid (matched against ${validate.regex})`);\n }\n }\n const contentDirNameFormat = options.dirNameFormat?.content;\n if (contentDirNameFormat) {\n const validate = FilenameFormatHelper.validateContentDirNameFormat(contentDirNameFormat);\n if (!validate.validateOK) {\n throw Error(`Content directory name format '${contentDirNameFormat}' is invalid (matched against ${validate.regex})`);\n }\n }\n const mediaFilenameFormat = options.filenameFormat?.media;\n if (mediaFilenameFormat) {\n const validate = FilenameFormatHelper.validateMediaFilenameFormat(mediaFilenameFormat);\n if (!validate.validateOK) {\n throw Error(`Media filename format '${mediaFilenameFormat}' is invalid (matched against ${validate.regex})`);\n }\n }\n\n return true;\n }\n\n protected saveCampaignInfo(campaign: Campaign | null, signal?: AbortSignal) {\n\n return new Promise<void>(async (resolve) => {\n if (!this.config.include.campaignInfo) {\n return;\n }\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n\n let batch: DownloadTaskBatch | null = null;\n const abortHandler = async () => {\n if (batch) {\n await batch.abort();\n }\n };\n if (signal) {\n signal.addEventListener('abort', abortHandler, { once: true });\n }\n\n if (!campaign) {\n this.log('warn', 'Skipped saving campaign info: target unavailable');\n return;\n }\n\n this.log('info', `Save campaign info #${campaign.id}`);\n this.emit('targetBegin', { target: campaign });\n this.emit('phaseBegin', { target: campaign, phase: 'saveInfo' });\n\n // Step 1: create campaign directories\n const campaignDirs = FSHelper.getCampaignDirs(campaign, this.config);\n this.log('debug', 'Campaign directories: ', campaignDirs);\n FSHelper.createDir(campaignDirs.root);\n FSHelper.createDir(campaignDirs.info);\n\n // Step 2: save summary and raw json\n const summary = generateCampaignSummary(campaign);\n const summaryFile = path.resolve(campaignDirs.info, 'info.txt');\n const saveSummaryResult = await FSHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);\n this.logWriteTextFileResult(saveSummaryResult, campaign, 'campaign summary');\n\n // Campaign / creator raw data might not be complete. Fetch directly from API.\n // Strictly speaking, we should check for 'error' in results, but since it's not going to be fatal we'll just skip it.\n const { json: fetchedCampaignAPIData } = await this.fetchCampaign(campaign.id, signal);\n const { json: fetchedCreatorAPIData } = campaign.creator ? await this.fetchUser(campaign.creator.id, signal) : { json: null };\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n\n const campaignRawFile = path.resolve(campaignDirs.info, 'campaign-api.json');\n const saveCampaignRawResult = await FSHelper.writeTextFile(\n campaignRawFile, fetchedCampaignAPIData || campaign.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(saveCampaignRawResult, campaign, 'campaign API data');\n\n if (campaign.creator) {\n const creatorRawFile = path.resolve(campaignDirs.info, 'creator-api.json');\n const saveCreatorRawResult = await FSHelper.writeTextFile(\n creatorRawFile, fetchedCreatorAPIData || campaign.creator.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(saveCreatorRawResult, campaign.creator, 'creator API data');\n }\n\n this.emit('phaseEnd', { target: campaign, phase: 'saveInfo' });\n\n // Step 3: download campaign media items\n this.emit('phaseBegin', { target: campaign, phase: 'saveMedia' });\n const campaignMedia: Downloadable[] = [\n campaign.avatarImage,\n campaign.coverPhoto\n ];\n if (campaign.creator) {\n campaignMedia.push(\n campaign.creator.image,\n campaign.creator.thumbnail\n );\n }\n for (const reward of campaign.rewards) {\n if (reward.image) {\n campaignMedia.push(reward.image);\n }\n }\n batch = this.createDownloadTaskBatch(\n `Campaign #${campaign.id} (${campaign.name})`,\n {\n target: campaignMedia,\n targetName: `campaign #${campaign.id} -> images`,\n destDir: campaignDirs.info,\n fileExistsAction: this.config.fileExistsAction.info\n }\n );\n this.emit('phaseBegin', { target: campaign, phase: 'batchDownload', batch });\n await batch.start();\n await batch.destroy();\n this.emit('phaseEnd', { target: campaign, phase: 'batchDownload' });\n this.emit('phaseEnd', { target: campaign, phase: 'saveMedia' });\n\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n\n // Done\n this.log('info', 'Done saving campaign info');\n this.emit('targetEnd', { target: campaign, isSkipped: false });\n\n resolve();\n });\n\n }\n\n protected log(level: LogLevel, ...msg: any[]) {\n commonLog(this.logger, level, this.name, ...msg);\n }\n\n getConfig() {\n return this.config;\n }\n\n protected checkAbortSignal(signal: AbortSignal | undefined, resolve: () => void) {\n if (signal && signal.aborted) {\n if (!this.#hasEmittedEndEventOnAbort) {\n this.emit('end', { aborted: true });\n this.#hasEmittedEndEventOnAbort = true;\n }\n resolve();\n return true;\n }\n return false;\n }\n\n protected logWriteTextFileResult<T>(result: WriteTextFileResult, target: T & {id: string}, targetName: string) {\n switch (result.status) {\n case 'completed':\n this.log('info', `Saved ${targetName} to \"${result.filePath}\"`);\n break;\n case 'skipped':\n this.log('warn', `Skipped saving ${targetName} #${target.id}: ${result.message}`);\n break;\n case 'error':\n this.log('error', `Error saving ${targetName} #${target.id} to \"${result.filePath}\":`, result.error);\n }\n }\n\n protected async commonFetchAPI(url: string, signal?: AbortSignal) {\n let json, requestAPIError: any;\n try {\n json = await this.fetcher.get({ url, type: 'json', maxRetries: this.config.request.maxRetries, signal });\n }\n catch (error) {\n if (error instanceof AbortError) {\n this.log('warn', 'API request aborted');\n }\n else {\n this.log('error', `Error requesting API URL \"${url}\": `, error);\n requestAPIError = error;\n }\n json = null;\n }\n return { json, error: requestAPIError };\n }\n\n protected fetchCampaign(campaignId: string, signal?: AbortSignal) {\n const url = URLHelper.constructCampaignAPIURL(campaignId);\n this.log('debug', `Fetch campaign data from API URL \"${url}\"`);\n return this.commonFetchAPI(url, signal);\n }\n\n protected fetchUser(userId: string, signal?: AbortSignal) {\n const url = URLHelper.constructUserAPIURL(userId);\n this.log('debug', `Fetch user data from API URL \"${url}\"`);\n return this.commonFetchAPI(url, signal);\n }\n\n on<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;\n on(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n once<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;\n once(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n off<T extends DownloaderEvent>(event: T, listener: (args: DownloaderEventPayloadOf<T>) => void): this;\n off(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.off(event, listener);\n }\n\n emit<T extends DownloaderEvent>(event: T, args: DownloaderEventPayloadOf<T>): boolean;\n emit(event: string | symbol, ...args: any[]): boolean {\n return super.emit(event, ...args);\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Campaign } from '../entities/Campaign.js';
|
|
2
|
+
import { Post } from '../entities/Post.js';
|
|
3
|
+
import { Product } from '../entities/Product.js';
|
|
4
|
+
import { IDownloadTaskBatch } from './task/DownloadTaskBatch.js';
|
|
5
|
+
export type DownloaderEvent = 'fetchBegin' | 'targetBegin' | 'targetEnd' | 'phaseBegin' | 'phaseEnd' | 'end';
|
|
6
|
+
export declare enum TargetSkipReason {
|
|
7
|
+
Inaccessible = 0,
|
|
8
|
+
AlreadyDownloaded = 1
|
|
9
|
+
}
|
|
10
|
+
export interface DownloaderEventPayload {
|
|
11
|
+
'fetchBegin': {
|
|
12
|
+
targetType: 'product' | 'post' | 'posts';
|
|
13
|
+
};
|
|
14
|
+
'targetBegin': {
|
|
15
|
+
target: Campaign | Product | Post;
|
|
16
|
+
};
|
|
17
|
+
'targetEnd': {
|
|
18
|
+
target: Campaign | Product | Post;
|
|
19
|
+
} & ({
|
|
20
|
+
isSkipped: false;
|
|
21
|
+
} | {
|
|
22
|
+
isSkipped: true;
|
|
23
|
+
skipReason: TargetSkipReason;
|
|
24
|
+
skipMessage: string;
|
|
25
|
+
});
|
|
26
|
+
'phaseBegin': {
|
|
27
|
+
target: Campaign | Product | Post;
|
|
28
|
+
} & ({
|
|
29
|
+
phase: 'saveInfo' | 'saveMedia';
|
|
30
|
+
} | {
|
|
31
|
+
phase: 'batchDownload';
|
|
32
|
+
batch: IDownloadTaskBatch;
|
|
33
|
+
});
|
|
34
|
+
'phaseEnd': {
|
|
35
|
+
target: Campaign | Product | Post;
|
|
36
|
+
phase: 'saveInfo' | 'saveMedia' | 'batchDownload';
|
|
37
|
+
};
|
|
38
|
+
'end': {
|
|
39
|
+
aborted: true;
|
|
40
|
+
error?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
aborted: false;
|
|
43
|
+
error?: any;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export type DownloaderEventPayloadOf<T extends DownloaderEvent> = DownloaderEventPayload[T];
|
|
47
|
+
//# sourceMappingURL=DownloaderEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloaderEvent.d.ts","sourceRoot":"","sources":["../../src/downloaders/DownloaderEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,MAAM,MAAM,eAAe,GACzB,YAAY,GACZ,aAAa,GACb,WAAW,GACX,YAAY,GACZ,UAAU,GACV,KAAK,CACJ;AAEH,oBAAY,gBAAgB;IAC1B,YAAY,IAAI;IAChB,iBAAiB,IAAI;CACtB;AAED,MAAM,WAAW,sBAAsB;IAErC,YAAY,EAAE;QACZ,UAAU,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;KAC1C,CAAA;IAED,aAAa,EAAE;QACb,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;KACnC,CAAC;IAEF,WAAW,EAAE;QACX,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;KACnC,GAAG,CAAC;QACH,SAAS,EAAE,KAAK,CAAC;KAClB,GAAG;QACF,SAAS,EAAE,IAAI,CAAC;QAChB,UAAU,EAAE,gBAAgB,CAAC;QAC7B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IAEH,YAAY,EAAE;QACZ,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;KACnC,GAAG,CAAC;QACH,KAAK,EAAE,UAAU,GAAG,WAAW,CAAC;KACjC,GAAG;QACF,KAAK,EAAE,eAAe,CAAC;QACvB,KAAK,EAAE,kBAAkB,CAAC;KAC3B,CAAC,CAAC;IAEH,UAAU,EAAE;QACV,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;QAClC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,eAAe,CAAC;KACnD,CAAC;IAEF,KAAK,EAAE;QACL,OAAO,EAAE,IAAI,CAAC;QACd,KAAK,CAAC,EAAE,SAAS,CAAC;KACnB,GAAG;QACF,OAAO,EAAE,KAAK,CAAC;QACf,KAAK,CAAC,EAAE,GAAG,CAAC;KACb,CAAC;CACH;AAED,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,eAAe,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export var TargetSkipReason;
|
|
2
|
+
(function (TargetSkipReason) {
|
|
3
|
+
TargetSkipReason[TargetSkipReason["Inaccessible"] = 0] = "Inaccessible";
|
|
4
|
+
TargetSkipReason[TargetSkipReason["AlreadyDownloaded"] = 1] = "AlreadyDownloaded";
|
|
5
|
+
})(TargetSkipReason || (TargetSkipReason = {}));
|
|
6
|
+
//# sourceMappingURL=DownloaderEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloaderEvent.js","sourceRoot":"","sources":["../../src/downloaders/DownloaderEvent.ts"],"names":[],"mappings":"AAcA,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,uEAAgB,CAAA;IAChB,iFAAqB,CAAA;AACvB,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B","sourcesContent":["import { Campaign } from '../entities/Campaign.js';\nimport { Post } from '../entities/Post.js';\nimport { Product } from '../entities/Product.js';\nimport { IDownloadTaskBatch } from './task/DownloadTaskBatch.js';\n\nexport type DownloaderEvent =\n 'fetchBegin' |\n 'targetBegin' |\n 'targetEnd' |\n 'phaseBegin' |\n 'phaseEnd' |\n 'end'\n ;\n\nexport enum TargetSkipReason {\n Inaccessible = 0,\n AlreadyDownloaded = 1\n}\n\nexport interface DownloaderEventPayload {\n\n 'fetchBegin': {\n targetType: 'product' | 'post' | 'posts';\n }\n\n 'targetBegin': {\n target: Campaign | Product | Post;\n };\n\n 'targetEnd': {\n target: Campaign | Product | Post;\n } & ({\n isSkipped: false;\n } | {\n isSkipped: true;\n skipReason: TargetSkipReason;\n skipMessage: string;\n });\n\n 'phaseBegin': {\n target: Campaign | Product | Post;\n } & ({\n phase: 'saveInfo' | 'saveMedia';\n } | {\n phase: 'batchDownload';\n batch: IDownloadTaskBatch;\n });\n\n 'phaseEnd': {\n target: Campaign | Product | Post;\n phase: 'saveInfo' | 'saveMedia' | 'batchDownload';\n };\n\n 'end': {\n aborted: true;\n error?: undefined;\n } | {\n aborted: false;\n error?: any;\n };\n}\n\nexport type DownloaderEventPayloadOf<T extends DownloaderEvent> = DownloaderEventPayload[T];\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Logger from '../utils/logging/Logger.js';
|
|
2
|
+
import { DeepRequired } from '../utils/Misc.js';
|
|
3
|
+
export type FileExistsAction = 'overwrite' | 'skip' | 'saveAsCopy' | 'saveAsCopyIfNewer';
|
|
4
|
+
export interface DownloaderOptions {
|
|
5
|
+
cookie?: string;
|
|
6
|
+
useStatusCache?: boolean;
|
|
7
|
+
pathToFFmpeg?: string | null;
|
|
8
|
+
outDir?: string;
|
|
9
|
+
dirNameFormat?: {
|
|
10
|
+
campaign?: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
};
|
|
13
|
+
filenameFormat?: {
|
|
14
|
+
media?: string;
|
|
15
|
+
};
|
|
16
|
+
include?: {
|
|
17
|
+
lockedContent?: boolean;
|
|
18
|
+
campaignInfo?: boolean;
|
|
19
|
+
contentInfo?: boolean;
|
|
20
|
+
previewMedia?: boolean;
|
|
21
|
+
contentMedia?: boolean;
|
|
22
|
+
allMediaVariants?: boolean;
|
|
23
|
+
};
|
|
24
|
+
request?: {
|
|
25
|
+
maxRetries?: number;
|
|
26
|
+
maxConcurrent?: number;
|
|
27
|
+
minTime?: number;
|
|
28
|
+
};
|
|
29
|
+
fileExistsAction?: {
|
|
30
|
+
content?: FileExistsAction;
|
|
31
|
+
info?: FileExistsAction;
|
|
32
|
+
infoAPI?: FileExistsAction;
|
|
33
|
+
};
|
|
34
|
+
logger?: Logger | null;
|
|
35
|
+
}
|
|
36
|
+
export type DownloaderInit = DeepRequired<Pick<DownloaderOptions, 'outDir' | 'useStatusCache' | 'pathToFFmpeg' | 'dirNameFormat' | 'filenameFormat' | 'include' | 'request' | 'fileExistsAction'>>;
|
|
37
|
+
export declare function getDownloaderInit(options?: DownloaderOptions): DownloaderInit;
|
|
38
|
+
export declare function getDefaultDownloaderOutDir(): string;
|
|
39
|
+
//# sourceMappingURL=DownloaderOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloaderOptions.d.ts","sourceRoot":"","sources":["../../src/downloaders/DownloaderOptions.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,4BAA4B,CAAC;AAChD,OAAO,EAAE,YAAY,EAAe,MAAM,kBAAkB,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,mBAAmB,CAAC;AAEzF,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAA;IACD,OAAO,CAAC,EAAE;QACR,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,gBAAgB,CAAC,EAAE;QACjB,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAC3B,IAAI,CAAC,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE,gBAAgB,CAAC;KAC5B,CAAA;IACD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAC9D,QAAQ,GACR,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,gBAAgB,GAChB,SAAS,GACT,SAAS,GACT,kBAAkB,CAAC,CAAC,CAAC;AAiCvB,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAgC7E;AAED,wBAAgB,0BAA0B,WAEzC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { pickDefined } from '../utils/Misc.js';
|
|
3
|
+
const DEFAULT_DOWNLOADER_INIT = {
|
|
4
|
+
outDir: process.cwd(),
|
|
5
|
+
useStatusCache: true,
|
|
6
|
+
pathToFFmpeg: null,
|
|
7
|
+
dirNameFormat: {
|
|
8
|
+
campaign: '{creator.vanity}[ - ]?{campaign.name}',
|
|
9
|
+
content: '{content.id}[ - ]?{content.name}'
|
|
10
|
+
},
|
|
11
|
+
filenameFormat: {
|
|
12
|
+
media: '{media.filename}'
|
|
13
|
+
},
|
|
14
|
+
include: {
|
|
15
|
+
lockedContent: true,
|
|
16
|
+
campaignInfo: true,
|
|
17
|
+
contentInfo: true,
|
|
18
|
+
previewMedia: true,
|
|
19
|
+
contentMedia: true,
|
|
20
|
+
allMediaVariants: false
|
|
21
|
+
},
|
|
22
|
+
request: {
|
|
23
|
+
maxRetries: 3,
|
|
24
|
+
maxConcurrent: 10,
|
|
25
|
+
minTime: 333
|
|
26
|
+
},
|
|
27
|
+
fileExistsAction: {
|
|
28
|
+
content: 'skip',
|
|
29
|
+
info: 'saveAsCopyIfNewer',
|
|
30
|
+
infoAPI: 'overwrite'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export function getDownloaderInit(options) {
|
|
34
|
+
const defaults = DEFAULT_DOWNLOADER_INIT;
|
|
35
|
+
return {
|
|
36
|
+
outDir: options?.outDir ? path.resolve(options.outDir) : defaults.outDir,
|
|
37
|
+
useStatusCache: pickDefined(options?.useStatusCache, defaults.useStatusCache),
|
|
38
|
+
pathToFFmpeg: pickDefined(options?.pathToFFmpeg, defaults.pathToFFmpeg),
|
|
39
|
+
dirNameFormat: {
|
|
40
|
+
campaign: options?.dirNameFormat?.campaign || defaults.dirNameFormat.campaign,
|
|
41
|
+
content: options?.dirNameFormat?.content || defaults.dirNameFormat.content
|
|
42
|
+
},
|
|
43
|
+
filenameFormat: {
|
|
44
|
+
media: options?.filenameFormat?.media || defaults.filenameFormat.media
|
|
45
|
+
},
|
|
46
|
+
include: {
|
|
47
|
+
lockedContent: pickDefined(options?.include?.lockedContent, defaults.include.lockedContent),
|
|
48
|
+
campaignInfo: pickDefined(options?.include?.campaignInfo, defaults.include.campaignInfo),
|
|
49
|
+
contentInfo: pickDefined(options?.include?.contentInfo, defaults.include.contentInfo),
|
|
50
|
+
previewMedia: pickDefined(options?.include?.previewMedia, defaults.include.previewMedia),
|
|
51
|
+
contentMedia: pickDefined(options?.include?.contentMedia, defaults.include.contentMedia),
|
|
52
|
+
allMediaVariants: pickDefined(options?.include?.allMediaVariants, defaults.include.allMediaVariants)
|
|
53
|
+
},
|
|
54
|
+
request: {
|
|
55
|
+
maxRetries: pickDefined(options?.request?.maxRetries, defaults.request.maxRetries),
|
|
56
|
+
maxConcurrent: pickDefined(options?.request?.maxConcurrent, defaults.request.maxConcurrent),
|
|
57
|
+
minTime: pickDefined(options?.request?.minTime, defaults.request.minTime)
|
|
58
|
+
},
|
|
59
|
+
fileExistsAction: {
|
|
60
|
+
content: options?.fileExistsAction?.content || defaults.fileExistsAction.content,
|
|
61
|
+
info: options?.fileExistsAction?.info || defaults.fileExistsAction.info,
|
|
62
|
+
infoAPI: options?.fileExistsAction?.infoAPI || defaults.fileExistsAction.infoAPI
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function getDefaultDownloaderOutDir() {
|
|
67
|
+
return DEFAULT_DOWNLOADER_INIT.outDir;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=DownloaderOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DownloaderOptions.js","sourceRoot":"","sources":["../../src/downloaders/DownloaderOptions.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport Logger from '../utils/logging/Logger.js';\nimport { DeepRequired, pickDefined } from '../utils/Misc.js';\n\nexport type FileExistsAction = 'overwrite' | 'skip' | 'saveAsCopy' | 'saveAsCopyIfNewer';\n\nexport interface DownloaderOptions {\n cookie?: string;\n useStatusCache?: boolean;\n pathToFFmpeg?: string | null;\n outDir?: string;\n dirNameFormat?: {\n campaign?: string;\n content?: string;\n };\n filenameFormat?: {\n media?: string;\n }\n include?: {\n lockedContent?: boolean;\n campaignInfo?: boolean;\n contentInfo?: boolean;\n previewMedia?: boolean;\n contentMedia?: boolean;\n allMediaVariants?: boolean;\n };\n request?: {\n maxRetries?: number;\n maxConcurrent?: number;\n minTime?: number;\n };\n fileExistsAction?: {\n content?: FileExistsAction;\n info?: FileExistsAction;\n infoAPI?: FileExistsAction;\n }\n logger?: Logger | null;\n}\n\nexport type DownloaderInit = DeepRequired<Pick<DownloaderOptions,\n 'outDir' |\n 'useStatusCache' |\n 'pathToFFmpeg' |\n 'dirNameFormat' |\n 'filenameFormat' |\n 'include' |\n 'request' |\n 'fileExistsAction'>>;\n\nconst DEFAULT_DOWNLOADER_INIT: DeepRequired<DownloaderInit> = {\n outDir: process.cwd(),\n useStatusCache: true,\n pathToFFmpeg: null,\n dirNameFormat: {\n campaign: '{creator.vanity}[ - ]?{campaign.name}',\n content: '{content.id}[ - ]?{content.name}'\n },\n filenameFormat: {\n media: '{media.filename}'\n },\n include: {\n lockedContent: true,\n campaignInfo: true,\n contentInfo: true,\n previewMedia: true,\n contentMedia: true,\n allMediaVariants: false\n },\n request: {\n maxRetries: 3,\n maxConcurrent: 10,\n minTime: 333\n },\n fileExistsAction: {\n content: 'skip',\n info: 'saveAsCopyIfNewer',\n infoAPI: 'overwrite'\n }\n};\n\nexport function getDownloaderInit(options?: DownloaderOptions): DownloaderInit {\n const defaults = DEFAULT_DOWNLOADER_INIT;\n return {\n outDir: options?.outDir ? path.resolve(options.outDir) : defaults.outDir,\n useStatusCache: pickDefined(options?.useStatusCache, defaults.useStatusCache),\n pathToFFmpeg: pickDefined(options?.pathToFFmpeg, defaults.pathToFFmpeg),\n dirNameFormat: {\n campaign: options?.dirNameFormat?.campaign || defaults.dirNameFormat.campaign,\n content: options?.dirNameFormat?.content || defaults.dirNameFormat.content\n },\n filenameFormat: {\n media: options?.filenameFormat?.media || defaults.filenameFormat.media\n },\n include: {\n lockedContent: pickDefined(options?.include?.lockedContent, defaults.include.lockedContent),\n campaignInfo: pickDefined(options?.include?.campaignInfo, defaults.include.campaignInfo),\n contentInfo: pickDefined(options?.include?.contentInfo, defaults.include.contentInfo),\n previewMedia: pickDefined(options?.include?.previewMedia, defaults.include.previewMedia),\n contentMedia: pickDefined(options?.include?.contentMedia, defaults.include.contentMedia),\n allMediaVariants: pickDefined(options?.include?.allMediaVariants, defaults.include.allMediaVariants)\n },\n request: {\n maxRetries: pickDefined(options?.request?.maxRetries, defaults.request.maxRetries),\n maxConcurrent: pickDefined(options?.request?.maxConcurrent, defaults.request.maxConcurrent),\n minTime: pickDefined(options?.request?.minTime, defaults.request.minTime)\n },\n fileExistsAction: {\n content: options?.fileExistsAction?.content || defaults.fileExistsAction.content,\n info: options?.fileExistsAction?.info || defaults.fileExistsAction.info,\n infoAPI: options?.fileExistsAction?.infoAPI || defaults.fileExistsAction.infoAPI\n }\n };\n}\n\nexport function getDefaultDownloaderOutDir() {\n return DEFAULT_DOWNLOADER_INIT.outDir;\n}"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Downloader, { DownloaderStartParams } from './Downloader.js';
|
|
2
|
+
export default class PostDownloader extends Downloader<'post'> {
|
|
3
|
+
#private;
|
|
4
|
+
static version: string;
|
|
5
|
+
name: string;
|
|
6
|
+
start(params?: DownloaderStartParams): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=PostDownloader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostDownloader.d.ts","sourceRoot":"","sources":["../../src/downloaders/PostDownloader.ts"],"names":[],"mappings":"AAGA,OAAO,UAAU,EAAE,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAYpE,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,UAAU,CAAC,MAAM,CAAC;;IAE5D,MAAM,CAAC,OAAO,SAAW;IAEzB,IAAI,SAAoB;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;CAkdrD"}
|