patreon-dl 3.7.0 → 3.7.1
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 +17 -1
- package/dist/browse/web/assets/index-CQnQD2xz.css +1 -0
- package/dist/browse/web/index.html +2 -2
- package/dist/cli/CLIOptions.js +2 -0
- package/dist/cli/CLIOptions.js.map +1 -1
- package/dist/cli/ConfigFileParser.js +2 -0
- package/dist/cli/ConfigFileParser.js.map +1 -1
- package/dist/downloaders/DownloaderOptions.d.ts +1 -0
- package/dist/downloaders/DownloaderOptions.js +2 -0
- package/dist/downloaders/DownloaderOptions.js.map +1 -1
- package/dist/downloaders/task/M3U8DownloadTask.d.ts +1 -0
- package/dist/downloaders/task/M3U8DownloadTask.js +137 -39
- package/dist/downloaders/task/M3U8DownloadTask.js.map +1 -1
- package/dist/parsers/PageParser.js +1 -1
- package/dist/parsers/PageParser.js.map +1 -1
- package/dist/utils/Fetcher.d.ts +4 -1
- package/dist/utils/Fetcher.js +6 -1
- package/dist/utils/Fetcher.js.map +1 -1
- package/package.json +1 -1
- package/dist/browse/web/assets/index-CW4CUoWl.css +0 -1
- /package/dist/browse/web/assets/{index-CziUWlSw.js → index-B1h15ViZ.js} +0 -0
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
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
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _M3U8DownloadTask_instances, _M3U8DownloadTask_fetcher, _M3U8DownloadTask_unresolvedDestFilePath, _M3U8DownloadTask_ffmpegCommandParams, _M3U8DownloadTask_pickVariant;
|
|
12
|
+
var _M3U8DownloadTask_instances, _M3U8DownloadTask_skipOnStart, _M3U8DownloadTask_fetcher, _M3U8DownloadTask_unresolvedDestFilePath, _M3U8DownloadTask_ffmpegCommandParams, _M3U8DownloadTask_pickVariant, _M3U8DownloadTask_getProtectionStatus, _M3U8DownloadTask_getResolutionString;
|
|
13
13
|
import { SITE_URL } from '../../utils/URLHelper.js';
|
|
14
14
|
import FFmpegDownloadTaskBase from './FFmpegDownloadTaskBase.js';
|
|
15
15
|
import semver from 'semver';
|
|
@@ -22,12 +22,23 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
|
|
|
22
22
|
super(params);
|
|
23
23
|
_M3U8DownloadTask_instances.add(this);
|
|
24
24
|
this.name = 'M3U8DownloadTask';
|
|
25
|
+
_M3U8DownloadTask_skipOnStart.set(this, void 0);
|
|
25
26
|
_M3U8DownloadTask_fetcher.set(this, void 0);
|
|
26
27
|
_M3U8DownloadTask_unresolvedDestFilePath.set(this, void 0);
|
|
27
28
|
_M3U8DownloadTask_ffmpegCommandParams.set(this, void 0);
|
|
28
29
|
__classPrivateFieldSet(this, _M3U8DownloadTask_fetcher, params.fetcher, "f");
|
|
29
30
|
__classPrivateFieldSet(this, _M3U8DownloadTask_unresolvedDestFilePath, params.destFilePath, "f");
|
|
30
31
|
__classPrivateFieldSet(this, _M3U8DownloadTask_ffmpegCommandParams, null, "f");
|
|
32
|
+
__classPrivateFieldSet(this, _M3U8DownloadTask_skipOnStart, null, "f");
|
|
33
|
+
}
|
|
34
|
+
async start() {
|
|
35
|
+
if (__classPrivateFieldGet(this, _M3U8DownloadTask_skipOnStart, "f")) {
|
|
36
|
+
return this.notifySkip({
|
|
37
|
+
name: 'other',
|
|
38
|
+
message: __classPrivateFieldGet(this, _M3U8DownloadTask_skipOnStart, "f").reason,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return await super.start();
|
|
31
42
|
}
|
|
32
43
|
async resolveDestPath(signal) {
|
|
33
44
|
const params = await this.getFFmpegCommandParams(signal);
|
|
@@ -38,6 +49,11 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
|
|
|
38
49
|
return __classPrivateFieldGet(this, _M3U8DownloadTask_ffmpegCommandParams, "f");
|
|
39
50
|
}
|
|
40
51
|
const input = await __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_pickVariant).call(this, signal);
|
|
52
|
+
if (input.src === null) {
|
|
53
|
+
__classPrivateFieldSet(this, _M3U8DownloadTask_skipOnStart, {
|
|
54
|
+
reason: `No downloadable source - ${input.reason}`
|
|
55
|
+
}, "f");
|
|
56
|
+
}
|
|
41
57
|
const inputOptions = [
|
|
42
58
|
'-protocol_whitelist',
|
|
43
59
|
'http,https,tcp,tls',
|
|
@@ -49,22 +65,38 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
|
|
|
49
65
|
inputOptions.push('-extension_picky', '0');
|
|
50
66
|
}
|
|
51
67
|
let output = __classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f");
|
|
52
|
-
if (
|
|
53
|
-
if (input.resolution) {
|
|
54
|
-
|
|
55
|
-
const {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
if (input.src) {
|
|
69
|
+
if (input.protected || input.resolution) {
|
|
70
|
+
const parsedFilePath = path.parse(__classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f"));
|
|
71
|
+
const { ext, dir } = parsedFilePath;
|
|
72
|
+
let filename = parsedFilePath.name;
|
|
73
|
+
if (input.resolution) {
|
|
74
|
+
filename += ` (${input.resolution})`;
|
|
75
|
+
}
|
|
76
|
+
if (input.protected) {
|
|
77
|
+
filename += ' drm';
|
|
78
|
+
}
|
|
79
|
+
output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename}${ext}`));
|
|
60
80
|
}
|
|
81
|
+
const criteria = JSON.stringify({
|
|
82
|
+
'max video resolution': this.config.maxVideoResolution || undefined,
|
|
83
|
+
'include protected media': this.config.include.protectedMedia ? 'yes' : undefined
|
|
84
|
+
});
|
|
85
|
+
const criteriaStr = criteria === '{}' ? '' : ` matching criteria ${JSON.stringify(criteria)}`;
|
|
86
|
+
const streamInfoParts = [
|
|
87
|
+
input.resolution || 'unknown resolution',
|
|
88
|
+
input.protected === true ? 'protected'
|
|
89
|
+
: input.protected === false ? ''
|
|
90
|
+
: 'unknown'
|
|
91
|
+
];
|
|
92
|
+
this.log('info', `Target stream${criteriaStr}:`, streamInfoParts.join(';'));
|
|
61
93
|
}
|
|
62
94
|
__classPrivateFieldSet(this, _M3U8DownloadTask_ffmpegCommandParams, {
|
|
63
95
|
inputs: [
|
|
64
96
|
{
|
|
65
|
-
input: input.src,
|
|
97
|
+
input: input.src || this.src,
|
|
66
98
|
options: inputOptions,
|
|
67
|
-
resolution: input.resolution
|
|
99
|
+
resolution: input.src ? input.resolution : null
|
|
68
100
|
}
|
|
69
101
|
],
|
|
70
102
|
output
|
|
@@ -75,54 +107,120 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
|
|
|
75
107
|
return this.srcEntity.duration;
|
|
76
108
|
}
|
|
77
109
|
}
|
|
78
|
-
_M3U8DownloadTask_fetcher = new WeakMap(), _M3U8DownloadTask_unresolvedDestFilePath = new WeakMap(), _M3U8DownloadTask_ffmpegCommandParams = new WeakMap(), _M3U8DownloadTask_instances = new WeakSet(), _M3U8DownloadTask_pickVariant = async function _M3U8DownloadTask_pickVariant(signal) {
|
|
79
|
-
const
|
|
80
|
-
if (!maxResolution || maxResolution < 0) {
|
|
81
|
-
return {
|
|
82
|
-
src: this.src,
|
|
83
|
-
resolution: null
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
this.log('debug', `Apply maxVideoResolution "${maxResolution}"`);
|
|
87
|
-
const { html: m3u8 } = await __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
|
|
110
|
+
_M3U8DownloadTask_skipOnStart = new WeakMap(), _M3U8DownloadTask_fetcher = new WeakMap(), _M3U8DownloadTask_unresolvedDestFilePath = new WeakMap(), _M3U8DownloadTask_ffmpegCommandParams = new WeakMap(), _M3U8DownloadTask_instances = new WeakSet(), _M3U8DownloadTask_pickVariant = async function _M3U8DownloadTask_pickVariant(signal) {
|
|
111
|
+
const { contents: m3u8 } = await __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
|
|
88
112
|
url: this.src,
|
|
89
|
-
type: '
|
|
113
|
+
type: 'm3u8',
|
|
90
114
|
maxRetries: this.config.request.maxRetries,
|
|
91
115
|
signal
|
|
92
116
|
});
|
|
93
117
|
const parser = new m3u8Parser.Parser();
|
|
94
118
|
parser.push(m3u8);
|
|
95
119
|
parser.end();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
if (!parser.manifest.playlists || parser.manifest.playlists.length === 0) {
|
|
121
|
+
this.log('warn', `No stream found in m3u8 manifest - going to download without stream selection`);
|
|
122
|
+
return {
|
|
123
|
+
src: this.src,
|
|
124
|
+
resolution: 'best quality',
|
|
125
|
+
protected: undefined
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const orderedPlaylistItems = parser.manifest.playlists
|
|
129
|
+
.sort((a, b) => {
|
|
130
|
+
// 1. Get heights or 0
|
|
131
|
+
const heightA = a.attributes.RESOLUTION?.height || 0;
|
|
132
|
+
const heightB = b.attributes.RESOLUTION?.height || 0;
|
|
133
|
+
// 2. If heights are different, sort by height
|
|
134
|
+
if (heightB !== heightA) {
|
|
135
|
+
return heightB - heightA;
|
|
136
|
+
}
|
|
137
|
+
// 3. If heights are the same (or both 0), sort by BANDWIDTH
|
|
138
|
+
const bandwidthA = a.attributes.BANDWIDTH || 0;
|
|
139
|
+
const bandwidthB = b.attributes.BANDWIDTH || 0;
|
|
140
|
+
return bandwidthB - bandwidthA;
|
|
141
|
+
});
|
|
142
|
+
let variants = await __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getProtectionStatus).call(this, orderedPlaylistItems, signal);
|
|
143
|
+
if (variants.every((v) => v.protected === false) && !this.config.maxVideoResolution) {
|
|
100
144
|
return {
|
|
101
145
|
src: this.src,
|
|
102
|
-
resolution:
|
|
146
|
+
resolution: 'best quality',
|
|
147
|
+
protected: false
|
|
103
148
|
};
|
|
104
149
|
}
|
|
150
|
+
this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);
|
|
151
|
+
// include.protectedMedia
|
|
152
|
+
if (!this.config.include.protectedMedia) {
|
|
153
|
+
const prevLength = variants.length;
|
|
154
|
+
variants = variants.filter((v) => !v.protected);
|
|
155
|
+
if (variants.length === 0) {
|
|
156
|
+
this.log('debug', 'All streams are protected');
|
|
157
|
+
return {
|
|
158
|
+
src: null,
|
|
159
|
+
reason: 'Media is protected'
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.log('debug', `${(prevLength - variants.length) / prevLength} streams are protected`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// maxVideoResolution
|
|
167
|
+
const maxResolution = this.config.maxVideoResolution;
|
|
168
|
+
const hasMaxResolutionConfigured = maxResolution && maxResolution > 0;
|
|
105
169
|
const __hasAudio = (variant) => {
|
|
106
170
|
const codecs = variant.attributes.CODECS || "";
|
|
107
171
|
return codecs.includes("mp4a"); // crude check for AAC audio
|
|
108
172
|
};
|
|
109
173
|
const allWithoutAudio = variants.every((v) => !__hasAudio(v));
|
|
110
|
-
|
|
111
|
-
.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
174
|
+
if (hasMaxResolutionConfigured) {
|
|
175
|
+
this.log('debug', `Apply maxVideoResolution "${maxResolution}"`);
|
|
176
|
+
const maxResCandidates = variants
|
|
177
|
+
.filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)));
|
|
178
|
+
if (maxResCandidates.length === 0) {
|
|
179
|
+
this.log('debug', `No stream in m3u8 manifest has resolution "${maxResolution}" or lower - maxVideoResolution not applied`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
variants = maxResCandidates;
|
|
183
|
+
}
|
|
119
184
|
}
|
|
120
|
-
const selected =
|
|
121
|
-
this.log('debug', `Video at or below resolution "${maxResolution}": ${selected.uri} (${JSON.stringify(selected.attributes.RESOLUTION)})`);
|
|
185
|
+
const selected = variants[0];
|
|
122
186
|
return {
|
|
123
187
|
src: new URL(selected.uri, this.src).href,
|
|
124
|
-
resolution:
|
|
188
|
+
resolution: __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getResolutionString).call(this, selected),
|
|
189
|
+
protected: selected.protected
|
|
125
190
|
};
|
|
191
|
+
}, _M3U8DownloadTask_getProtectionStatus = async function _M3U8DownloadTask_getProtectionStatus(variants, signal) {
|
|
192
|
+
return await Promise.all(variants.map((variant) => __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
|
|
193
|
+
url: variant.uri,
|
|
194
|
+
type: 'm3u8',
|
|
195
|
+
maxRetries: this.config.request.maxRetries,
|
|
196
|
+
signal
|
|
197
|
+
})
|
|
198
|
+
.then(({ contents: m3u8 }) => {
|
|
199
|
+
const parser = new m3u8Parser.Parser();
|
|
200
|
+
parser.push(m3u8);
|
|
201
|
+
parser.end();
|
|
202
|
+
const protectionData = parser.manifest.contentProtection;
|
|
203
|
+
const _protected = !!(protectionData && typeof protectionData === 'object' && Object.entries(protectionData).length > 0);
|
|
204
|
+
return {
|
|
205
|
+
...variant,
|
|
206
|
+
protected: _protected
|
|
207
|
+
};
|
|
208
|
+
})
|
|
209
|
+
.catch((error) => {
|
|
210
|
+
if (signal?.aborted) {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
this.log('warn', `Could not determine if stream (${__classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getResolutionString).call(this, variant)}) is protected:`, error);
|
|
214
|
+
return {
|
|
215
|
+
...variant,
|
|
216
|
+
protected: undefined
|
|
217
|
+
};
|
|
218
|
+
})));
|
|
219
|
+
}, _M3U8DownloadTask_getResolutionString = function _M3U8DownloadTask_getResolutionString(item) {
|
|
220
|
+
if (item.attributes.RESOLUTION?.width && item.attributes.RESOLUTION?.height) {
|
|
221
|
+
return `${item.attributes.RESOLUTION.width}x${item.attributes.RESOLUTION.height}`;
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
126
224
|
};
|
|
127
225
|
export default M3U8DownloadTask;
|
|
128
226
|
//# sourceMappingURL=M3U8DownloadTask.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"M3U8DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/M3U8DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { type VideoMediaItem } from '../../entities/MediaItem.js';\nimport { SITE_URL } from '../../utils/URLHelper.js';\nimport FFmpegDownloadTaskBase, { type FFmpegCommandParams, type FFmpegDownloadTaskBaseParams } from './FFmpegDownloadTaskBase.js';\nimport semver from 'semver';\nimport m3u8Parser from 'm3u8-parser';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport path from 'path';\n\nexport interface M3U8DownloadTaskParams extends FFmpegDownloadTaskBaseParams<VideoMediaItem> {\n fetcher: Fetcher;\n destFilePath: string;\n}\n\ninterface M3U8FFmpegCommandParams extends FFmpegCommandParams {\n inputs: (FFmpegCommandParams['inputs'][number] & { resolution: string | null })[];\n};\n\nexport default class M3U8DownloadTask extends FFmpegDownloadTaskBase<VideoMediaItem> {\n\n name = 'M3U8DownloadTask';\n\n #fetcher: Fetcher;\n #unresolvedDestFilePath: string;\n #ffmpegCommandParams: M3U8FFmpegCommandParams | null;\n\n constructor(params: M3U8DownloadTaskParams) {\n super(params);\n this.#fetcher = params.fetcher;\n this.#unresolvedDestFilePath = params.destFilePath;\n this.#ffmpegCommandParams = null;\n }\n\n protected async resolveDestPath(signal?: AbortSignal) {\n const params = await this.getFFmpegCommandParams(signal);\n return params.output;\n }\n\n protected async getFFmpegCommandParams(signal?: AbortSignal): Promise<M3U8FFmpegCommandParams> {\n if (this.#ffmpegCommandParams) {\n return this.#ffmpegCommandParams;\n }\n const input = await this.#pickVariant(signal);\n const inputOptions = [\n '-protocol_whitelist',\n 'http,https,tcp,tls',\n '-headers',\n `Referer: ${SITE_URL}`\n ];\n // `extension_picky` introduced in v7.1.1\n if (semver.satisfies(this.getFFmpegVersion(), '>=7.1.1')) {\n inputOptions.push('-extension_picky', '0');\n }\n let output = this.#unresolvedDestFilePath;\n if (this.config.maxVideoResolution && this.config.maxVideoResolution > 0) {\n if (input.resolution) {\n this.log('info', `Video found with resolution at or below specified max resolution \"${this.config.maxVideoResolution}\": ${input.resolution}`);\n const { name: filename, ext, dir } = path.parse(this.#unresolvedDestFilePath);\n output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename} (${input.resolution})${ext}`));\n }\n else {\n this.log('info', `No video found with resolution at or below specified max resolution \"${this.config.maxVideoResolution}\" - going to download highest`);\n }\n }\n\n this.#ffmpegCommandParams = {\n inputs: [\n {\n input: input.src,\n options: inputOptions,\n resolution: input.resolution\n }\n ],\n output\n };\n\n return this.#ffmpegCommandParams;\n }\n\n protected getTargetDuration(): number | null {\n return this.srcEntity.duration;\n }\n\n async #pickVariant(signal?: AbortSignal) {\n const maxResolution = this.config.maxVideoResolution;\n if (!maxResolution || maxResolution < 0) {\n return {\n src: this.src,\n resolution: null\n };\n }\n\n this.log('debug', `Apply maxVideoResolution \"${maxResolution}\"`);\n\n const { html: m3u8 } = await this.#fetcher.get({\n url: this.src,\n type: 'html',\n maxRetries: this.config.request.maxRetries,\n signal\n });\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n\n const variants = parser.manifest.playlists;\n\n this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);\n \n if (!variants) {\n this.log('debug', `No videos found in m3u8 manifest - going to download video with highest resolution`);\n return {\n src: this.src,\n resolution: null\n };\n }\n \n const __hasAudio = (variant: typeof variants[number]) => {\n const codecs = variant.attributes.CODECS || \"\";\n return codecs.includes(\"mp4a\"); // crude check for AAC audio\n }\n\n const allWithoutAudio = variants.every((v) => !__hasAudio(v));\n\n const filtered = variants\n .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)))\n .sort((a, b) => b.attributes.RESOLUTION!.height - a.attributes.RESOLUTION!.height);\n\n if (filtered.length === 0) {\n this.log('debug', `No video found in m3u8 manifest at or below resolution \"${maxResolution}\" - going to download video with highest resolution`);\n return {\n src: this.src,\n resolution: null\n };\n }\n\n const selected = filtered[0];\n\n this.log('debug', `Video at or below resolution \"${maxResolution}\": ${selected.uri} (${JSON.stringify(selected.attributes.RESOLUTION)})`);\n\n return {\n src: new URL(selected.uri, this.src).href,\n resolution: `${selected.attributes.RESOLUTION!.width}x${selected.attributes.RESOLUTION!.height}`\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"M3U8DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/M3U8DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { type VideoMediaItem } from '../../entities/MediaItem.js';\nimport { SITE_URL } from '../../utils/URLHelper.js';\nimport FFmpegDownloadTaskBase, { type FFmpegCommandParams, type FFmpegDownloadTaskBaseParams } from './FFmpegDownloadTaskBase.js';\nimport semver from 'semver';\nimport m3u8Parser, { type PlaylistItem } from 'm3u8-parser';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport path from 'path';\n\ninterface Variant extends PlaylistItem {\n protected?: boolean;\n}\n\ntype PickVariantResult = {\n src: null;\n reason: string;\n} | {\n src: string;\n resolution: string | null,\n protected?: boolean;\n};\n\nexport interface M3U8DownloadTaskParams extends FFmpegDownloadTaskBaseParams<VideoMediaItem> {\n fetcher: Fetcher;\n destFilePath: string;\n}\n\ninterface M3U8FFmpegCommandParams extends FFmpegCommandParams {\n inputs: (FFmpegCommandParams['inputs'][number] & { resolution: string | null })[];\n};\n\nexport default class M3U8DownloadTask extends FFmpegDownloadTaskBase<VideoMediaItem> {\n\n name = 'M3U8DownloadTask';\n\n #skipOnStart: {\n reason: string;\n } | null;\n #fetcher: Fetcher;\n #unresolvedDestFilePath: string;\n #ffmpegCommandParams: M3U8FFmpegCommandParams | null;\n\n constructor(params: M3U8DownloadTaskParams) {\n super(params);\n this.#fetcher = params.fetcher;\n this.#unresolvedDestFilePath = params.destFilePath;\n this.#ffmpegCommandParams = null;\n this.#skipOnStart = null;\n }\n\n async start() {\n if (this.#skipOnStart) {\n return this.notifySkip({\n name: 'other',\n message: this.#skipOnStart.reason,\n });\n }\n return await super.start();\n }\n\n protected async resolveDestPath(signal?: AbortSignal) {\n const params = await this.getFFmpegCommandParams(signal);\n return params.output;\n }\n\n protected async getFFmpegCommandParams(signal?: AbortSignal): Promise<M3U8FFmpegCommandParams> {\n if (this.#ffmpegCommandParams) {\n return this.#ffmpegCommandParams;\n }\n\n const input = await this.#pickVariant(signal);\n if (input.src === null) {\n this.#skipOnStart = {\n reason: `No downloadable source - ${input.reason}`\n };\n }\n\n const inputOptions = [\n '-protocol_whitelist',\n 'http,https,tcp,tls',\n '-headers',\n `Referer: ${SITE_URL}`\n ];\n // `extension_picky` introduced in v7.1.1\n if (semver.satisfies(this.getFFmpegVersion(), '>=7.1.1')) {\n inputOptions.push('-extension_picky', '0');\n }\n let output = this.#unresolvedDestFilePath;\n if (input.src) {\n if (input.protected || input.resolution) {\n const parsedFilePath = path.parse(this.#unresolvedDestFilePath);\n const { ext, dir } = parsedFilePath;\n let filename = parsedFilePath.name;\n if (input.resolution) {\n filename += ` (${input.resolution})`;\n }\n if (input.protected) {\n filename += ' drm';\n }\n output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename}${ext}`));\n }\n\n const criteria = JSON.stringify({\n 'max video resolution': this.config.maxVideoResolution || undefined,\n 'include protected media': this.config.include.protectedMedia ? 'yes' : undefined\n });\n const criteriaStr = criteria === '{}' ? '' : ` matching criteria ${JSON.stringify(criteria)}`;\n const streamInfoParts = [\n input.resolution || 'unknown resolution',\n input.protected === true ? 'protected'\n : input.protected === false ? ''\n : 'unknown'\n ];\n this.log('info', `Target stream${criteriaStr}:`, streamInfoParts.join(';'));\n }\n\n this.#ffmpegCommandParams = {\n inputs: [\n {\n input: input.src || this.src,\n options: inputOptions,\n resolution: input.src ? input.resolution : null\n }\n ],\n output\n };\n\n return this.#ffmpegCommandParams;\n }\n\n protected getTargetDuration(): number | null {\n return this.srcEntity.duration;\n }\n\n async #pickVariant(signal?: AbortSignal): Promise<PickVariantResult> {\n const { contents: m3u8 } = await this.#fetcher.get({\n url: this.src,\n type: 'm3u8',\n maxRetries: this.config.request.maxRetries,\n signal\n });\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n\n if (!parser.manifest.playlists || parser.manifest.playlists.length === 0) {\n this.log('warn', `No stream found in m3u8 manifest - going to download without stream selection`);\n return {\n src: this.src,\n resolution: 'best quality',\n protected: undefined\n };\n }\n\n const orderedPlaylistItems = parser.manifest.playlists\n .sort((a, b) => {\n // 1. Get heights or 0\n const heightA = a.attributes.RESOLUTION?.height || 0;\n const heightB = b.attributes.RESOLUTION?.height || 0;\n\n // 2. If heights are different, sort by height\n if (heightB !== heightA) {\n return heightB - heightA;\n }\n\n // 3. If heights are the same (or both 0), sort by BANDWIDTH\n const bandwidthA = a.attributes.BANDWIDTH || 0;\n const bandwidthB = b.attributes.BANDWIDTH || 0;\n\n return bandwidthB - bandwidthA;\n });\n let variants = await this.#getProtectionStatus(orderedPlaylistItems, signal);\n \n if (variants.every((v) => v.protected === false) && !this.config.maxVideoResolution) {\n return {\n src: this.src,\n resolution: 'best quality',\n protected: false\n };\n }\n\n this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);\n \n // include.protectedMedia\n if (!this.config.include.protectedMedia) {\n const prevLength = variants.length;\n variants = variants.filter((v) => !v.protected)\n if (variants.length === 0) {\n this.log('debug', 'All streams are protected');\n return {\n src: null,\n reason: 'Media is protected'\n };\n }\n else {\n this.log('debug', `${(prevLength - variants.length) / prevLength} streams are protected`)\n }\n }\n\n // maxVideoResolution\n const maxResolution = this.config.maxVideoResolution;\n const hasMaxResolutionConfigured = maxResolution && maxResolution > 0;\n \n const __hasAudio = (variant: typeof variants[number]) => {\n const codecs = variant.attributes.CODECS || \"\";\n return codecs.includes(\"mp4a\"); // crude check for AAC audio\n }\n\n const allWithoutAudio = variants.every((v) => !__hasAudio(v));\n\n if (hasMaxResolutionConfigured) {\n this.log('debug', `Apply maxVideoResolution \"${maxResolution}\"`);\n const maxResCandidates = variants\n .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)));\n if (maxResCandidates.length === 0 ) {\n this.log('debug', `No stream in m3u8 manifest has resolution \"${maxResolution}\" or lower - maxVideoResolution not applied`);\n }\n else {\n variants = maxResCandidates;\n }\n }\n\n const selected = variants[0];\n\n return {\n src: new URL(selected.uri, this.src).href,\n resolution: this.#getResolutionString(selected),\n protected: selected.protected\n };\n }\n\n async #getProtectionStatus(variants: PlaylistItem[], signal?: AbortSignal): Promise<Variant[]> {\n return await Promise.all(variants.map((variant) =>\n this.#fetcher.get({\n url: variant.uri,\n type: 'm3u8',\n maxRetries: this.config.request.maxRetries,\n signal\n })\n .then(({contents: m3u8}) => {\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n const protectionData = parser.manifest.contentProtection;\n const _protected = !!(protectionData && typeof protectionData === 'object' && Object.entries(protectionData).length > 0);\n return {\n ...variant,\n protected: _protected\n };\n })\n .catch((error: unknown) => {\n if (signal?.aborted) {\n throw error;\n }\n this.log('warn', `Could not determine if stream (${this.#getResolutionString(variant)}) is protected:`, error);\n return {\n ...variant,\n protected: undefined\n };\n })\n ));\n }\n\n #getResolutionString(item: PlaylistItem) {\n if (item.attributes.RESOLUTION?.width && item.attributes.RESOLUTION?.height) {\n return `${item.attributes.RESOLUTION.width}x${item.attributes.RESOLUTION.height}`;\n }\n return null;\n }\n}\n"]}
|
|
@@ -59,7 +59,7 @@ class PageParser extends Parser {
|
|
|
59
59
|
throw Error(`Failed to obtain initial data - "currentUserId" not found in Next.js streaming response`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
const campaignIdRegex = /
|
|
62
|
+
const campaignIdRegex = /{\\"self\\":\\"https:\/\/www\.patreon\.com\/api\/campaigns\/(.+?)\\"}/gm;
|
|
63
63
|
this.log('debug', `Trying pattern in Next.js streaming response: ${campaignIdRegex}`);
|
|
64
64
|
const campaignIdMatch = campaignIdRegex.exec(html);
|
|
65
65
|
if (!campaignIdMatch || !campaignIdMatch[1]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageParser.js","sourceRoot":"","sources":["../../src/parsers/PageParser.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PageParser.js","sourceRoot":"","sources":["../../src/parsers/PageParser.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,YAAY,MAAM,0BAA0B,CAAC;AACpD,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAqB,UAAW,SAAQ,MAAM;IAA9C;;;QAEY,SAAI,GAAG,YAAY,CAAC;IAiHhC,CAAC;IA/GC,gBAAgB,CAAC,IAAY,EAAE,IAAY;QACzC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,2BAA2B,IAAI,EAAE,CAAC,CAAC;QAErD,MAAM,gBAAgB,GAAG,oCAAoC,CAAC;QAC9D,MAAM,iBAAiB,GAAG,sEAAsE,CAAC;QAEjG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,gBAAgB,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO,KAAU,EAAE,CAAC;gBAClB,MAAM,KAAK,CAAC,gBAAgB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,yBAAyB,gBAAgB,EAAE,CAAC,CAAC;QAE/D,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,iBAAiB,EAAE,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrC,OAAO,YAAY,CAAC,WAAW,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,KAAU,EAAE,CAAC;gBAClB,MAAM,KAAK,CAAC,gBAAgB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;QAEhE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,kCAAkC,CAAC,CAAC;QACtD,MAAM,yBAAyB,GAAG,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QACtE,IAAI,yBAAyB,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,qCAAqC,CAAC,CAAC;YAEzD,IAAI,aAAa,GAAkB,IAAI,CAAC;YACxC,KAAK,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,uBAAA,IAAI,6DAAoB,MAAxB,IAAI,EAAqB,IAAI,EAAE,aAAa,CAAC,EAAE,CAAC;gBAClF,IAAI,WAAW,EAAE,CAAC;oBAChB,aAAa,GAAG,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;oBACjE,IAAI,aAAa,EAAE,CAAC;wBAClB,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YACD,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,MAAM,yBAAyB,GAAG,+BAA+B,CAAC;gBAClE,MAAM,oBAAoB,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAClE,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBAC1B,MAAM,KAAK,CAAC,yFAAyF,CAAC,CAAC;gBACzG,CAAC;YACH,CAAC;YAED,MAAM,eAAe,GAAG,yEAAyE,CAAC;YAClG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,iDAAiD,eAAe,EAAE,CAAC,CAAC;YACtF,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,CAAC,eAAe,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5C,MAAM,KAAK,CAAC,gFAAgF,eAAe,EAAE,CAAC,CAAC;YACjH,CAAC;YAED,MAAM,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpE,OAAO;gBACL,aAAa,EAAE;oBACb,QAAQ,EAAE;wBACR,IAAI,EAAE;4BACJ,EAAE,EAAE,UAAU;yBACf;qBACF;iBACF;gBACD,eAAe,EAAE;oBACf,WAAW,EAAE;wBACX,IAAI,EAAE;4BACJ,EAAE,EAAE,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;yBACnF;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC3D,CAAC;CA+BF;gFA7BC,CAAC,gCAAoB,KAAa,EAAE,GAAW;IAC7C,MAAM,UAAU,GAAG,QAAQ,GAAG,iBAAiB,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAE7C,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,wBAAwB;QACzE,IAAI,GAAG,GAAG,KAAK,CAAC;QAChB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,OAAO,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YAC5C,GAAG,EAAE,CAAC;YACN,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG;gBAAE,UAAU,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG;gBAAE,UAAU,EAAE,CAAC;QACvC,CAAC;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC;YACH,MAAM;gBACJ,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;aAC/B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACzD,MAAM;gBACJ,IAAI,EAAE,IAAI;aACX,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;eAlHkB,UAAU","sourcesContent":["import ObjectHelper from '../utils/ObjectHelper.js';\nimport Parser from './Parser.js';\n\nexport default class PageParser extends Parser {\n\n protected name = 'PageParser';\n\n parseInitialData(html: string, _url: string) {\n this.log('debug', `Parse initial data from ${_url}`);\n\n const initialDataRegex = /window\\.patreon\\s*?=\\s*?({.+?});/gm;\n const initialDataRegex2 = /<script id=\"__NEXT_DATA__\" type=\"application\\/json\">(.+)<\\/script>/gm;\n\n this.log('debug', `Trying pattern: ${initialDataRegex}`);\n const match = initialDataRegex.exec(html);\n if (match && match[1]) {\n try {\n return JSON.parse(match[1]);\n }\n catch (error: any) {\n throw Error(`Parse error: ${error instanceof Error ? error.message : error}`);\n }\n }\n this.log('debug', `No match for pattern: ${initialDataRegex}`);\n\n this.log('debug', `Trying pattern: ${initialDataRegex2}`);\n const match2 = initialDataRegex2.exec(html);\n if (match2 && match2[1]) {\n try {\n const parsed = JSON.parse(match2[1]);\n return ObjectHelper.getProperty(parsed, 'props.pageProps.bootstrapEnvelope');\n }\n catch (error: any) {\n throw Error(`Parse error: ${error instanceof Error ? error.message : error}`);\n }\n }\n this.log('debug', `No match for pattern: ${initialDataRegex2}`);\n\n this.log('debug', 'Check Next.js streaming response');\n const isNextJSStreamingResponse = html.includes('self.__next_f.push');\n if (isNextJSStreamingResponse) {\n this.log('debug', 'Detected Next.js streaming response');\n\n let currentUserId: string | null = null;\n for (const { data: currentUser } of this.#extractJsonFromKey(html, 'currentUser')) {\n if (currentUser) {\n currentUserId = ObjectHelper.getProperty(currentUser, 'data.id');\n if (currentUserId) {\n break;\n }\n }\n }\n if (!currentUserId) {\n const matchCurrentUserNullRegex = /\\\\\"currentUser\\\\\"\\s*:\\s*null/g;\n const matchCurrentUserNull = matchCurrentUserNullRegex.exec(html);\n if (!matchCurrentUserNull) {\n throw Error(`Failed to obtain initial data - \"currentUserId\" not found in Next.js streaming response`);\n }\n }\n\n const campaignIdRegex = /{\\\\\"self\\\\\":\\\\\"https:\\/\\/www\\.patreon\\.com\\/api\\/campaigns\\/(.+?)\\\\\"}/gm;\n this.log('debug', `Trying pattern in Next.js streaming response: ${campaignIdRegex}`);\n const campaignIdMatch = campaignIdRegex.exec(html);\n if (!campaignIdMatch || !campaignIdMatch[1]) {\n throw Error(`Initial data not found - no match for pattern in Next.js streaming response: ${campaignIdRegex}`);\n }\n\n const campaignId = campaignIdMatch ? campaignIdMatch[1] : undefined;\n return {\n pageBootstrap: {\n campaign: {\n data: {\n id: campaignId\n }\n }\n },\n commonBootstrap: {\n currentUser: {\n data: {\n id: currentUserId === null || currentUserId === 'null' ? undefined : currentUserId\n }\n }\n }\n };\n }\n\n throw Error('Initial data not found - no regex matches');\n }\n\n *#extractJsonFromKey(input: string, key: string) {\n const keyPattern = `\\\\\\\\\"${key}\\\\\\\\\"\\\\s*:\\\\s*{`;\n const keyRegex = new RegExp(keyPattern, 'g');\n\n let match;\n while ((match = keyRegex.exec(input)) !== null) {\n const start = match.index + match[0].length - 1; // position of opening {\n let end = start;\n let braceCount = 1;\n\n while (end < input.length && braceCount > 0) {\n end++;\n if (input[end] === '{') braceCount++;\n if (input[end] === '}') braceCount--;\n }\n\n const jsonFragment = input.slice(start, end + 1).replaceAll('\\\\', '');\n try {\n yield {\n data: JSON.parse(jsonFragment)\n };\n } catch (error) {\n this.log('debug', 'Error parsing JSON fragment:', error);\n yield {\n data: null,\n };\n }\n }\n }\n}\n"]}
|
package/dist/utils/Fetcher.d.ts
CHANGED
|
@@ -21,10 +21,13 @@ export declare class FetcherError extends Error {
|
|
|
21
21
|
method: string;
|
|
22
22
|
constructor(message: string, url: string, method: string);
|
|
23
23
|
}
|
|
24
|
-
export type FetcherGetType = 'html' | 'json';
|
|
24
|
+
export type FetcherGetType = 'html' | 'm3u8' | 'json';
|
|
25
25
|
export type FetcherGetResultOf<T extends FetcherGetType> = T extends 'html' ? {
|
|
26
26
|
html: string;
|
|
27
27
|
lastUrl: string;
|
|
28
|
+
} : T extends 'm3u8' ? {
|
|
29
|
+
contents: string;
|
|
30
|
+
lastUrl: string;
|
|
28
31
|
} : T extends 'json' ? {
|
|
29
32
|
json: any;
|
|
30
33
|
lastUrl: string;
|
package/dist/utils/Fetcher.js
CHANGED
|
@@ -56,7 +56,7 @@ class Fetcher {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
const request = new Request(urlObj, { method: 'GET' });
|
|
59
|
-
__classPrivateFieldGet(this, _Fetcher_instances, "m", _Fetcher_setHeaders).call(this, request, type);
|
|
59
|
+
__classPrivateFieldGet(this, _Fetcher_instances, "m", _Fetcher_setHeaders).call(this, request, type, type === 'm3u8' ? { setReferer: true } : {});
|
|
60
60
|
const internalAbortController = new AbortController();
|
|
61
61
|
let removeAbortHandler = undefined;
|
|
62
62
|
if (signal) {
|
|
@@ -73,6 +73,11 @@ class Fetcher {
|
|
|
73
73
|
html: await res.text(),
|
|
74
74
|
lastUrl
|
|
75
75
|
};
|
|
76
|
+
case 'm3u8':
|
|
77
|
+
return {
|
|
78
|
+
contents: await res.text(),
|
|
79
|
+
lastUrl
|
|
80
|
+
};
|
|
76
81
|
case 'json':
|
|
77
82
|
return {
|
|
78
83
|
json: await res.json(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Fetcher.js","sourceRoot":"","sources":["../../src/utils/Fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { pipeline } from 'stream/promises';\nimport { fetch, Request, type Response } from 'undici';\nimport path from 'path';\nimport { type Dispatcher } from 'undici';\nimport puppeteer from 'puppeteer';\nimport FetcherProgressMonitor from './FetcherProgressMonitor.js';\nimport { type Downloadable } from '../entities/Downloadable.js';\nimport type FilenameResolver from './FllenameResolver.js';\nimport { pickDefined } from './Misc.js';\nimport {type LogLevel} from './logging/Logger.js';\nimport type Logger from './logging/Logger.js';\nimport { commonLog } from './logging/Logger.js';\nimport FSHelper from './FSHelper.js';\nimport { type DownloaderConfig } from '../downloaders/Downloader.js';\nimport Progress from './Progress.js';\nimport { createProxyAgent } from './Proxy.js';\nimport { SITE_URL } from './URLHelper.js';\n\nexport interface PrepareDownloadParams<T extends Downloadable> {\n url: string;\n srcEntity: T;\n destFilePath: string;\n setReferer?: boolean;\n signal: AbortSignal;\n}\n\nexport interface StartDownloadOverrides {\n destFilePath?: string;\n tmpFilePath?: string;\n}\n\nexport class FetcherError extends Error {\n\n url: string;\n method: string;\n\n constructor(message: string, url: string, method: string) {\n super(message);\n this.name = 'FetcherError';\n this.url = url;\n this.method = method;\n }\n}\n\nexport type FetcherGetType = 'html' | 'json';\nexport type FetcherGetResultOf<T extends FetcherGetType> =\n T extends 'html' ? { html: string; lastUrl: string; }:\n T extends 'json' ? { json: any; lastUrl: string; } :\n never;\n\nexport default class Fetcher {\n\n name = 'Fetcher';\n\n #cookie?: string;\n #logger?: Logger | null;\n #dryRun: boolean;\n #fsHelper: FSHelper;\n #proxyAgent?: Dispatcher;\n #userAgent: string;\n\n constructor(config: DownloaderConfig<any>, logger?: Logger | null) {\n this.#cookie = config.cookie;\n this.#logger = logger;\n this.#dryRun = config.dryRun;\n this.#fsHelper = new FSHelper(config, logger);\n this.#proxyAgent = createProxyAgent(config)?.agent || undefined;\n this.#userAgent = config.request.userAgent;\n }\n\n async get<T extends FetcherGetType>(args: {\n url: string,\n type: T,\n payload?: Record<string, any>,\n maxRetries: number,\n signal?: AbortSignal\n }, rt = 0): Promise<FetcherGetResultOf<T>> {\n\n const { url, type, payload, maxRetries, signal } = args;\n\n const urlObj = new URL(url);\n if (payload) {\n for (const [ p, v ] of Object.entries(payload)) {\n urlObj.searchParams.set(p, v);\n }\n }\n const request = new Request(urlObj, { method: 'GET' });\n this.#setHeaders(request, type);\n const internalAbortController = new AbortController();\n let removeAbortHandler: undefined | (() => void) = undefined;\n if (signal) {\n const abortHandler = () => internalAbortController.abort();\n signal.addEventListener('abort', abortHandler, {once: true});\n removeAbortHandler = () => signal.removeEventListener('abort', abortHandler);\n }\n try {\n const res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n const lastUrl = res.url;\n switch (type) {\n case 'html':\n return {\n html: await res.text(),\n lastUrl\n } as FetcherGetResultOf<T>;\n case 'json':\n return {\n json: await res.json(),\n lastUrl\n } as FetcherGetResultOf<T>;\n default:\n return undefined as never;\n }\n }\n catch (error: any) {\n if (signal?.aborted) {\n throw error;\n }\n if (rt < maxRetries) {\n if (removeAbortHandler) removeAbortHandler();\n return await this.get({ url, type, payload, maxRetries, signal }, rt + 1);\n }\n let errMsg;\n if (error instanceof Error) {\n const errMsgParts = [error.message];\n let _err = error.cause;\n while (_err) {\n errMsgParts.push(_err instanceof Error ? _err.message : JSON.stringify(_err));\n _err = _err instanceof Error ? _err.cause : null;\n }\n errMsg = errMsgParts.join(': ');\n }\n else {\n errMsg = String(error);\n }\n const retriedMsg = rt > 0 ? ` (retried ${rt} times)` : '';\n throw new FetcherError(`${errMsg}${retriedMsg}`, urlObj.toString(), request.method);\n }\n finally {\n if (removeAbortHandler) removeAbortHandler();\n }\n }\n\n async resolveDestFilePath<T extends Downloadable>(params: {\n url: string;\n destDir: string;\n destFilenameResolver: FilenameResolver<T>;\n setReferer?: boolean;\n signal?: AbortSignal;\n }) {\n const { url, destDir, destFilenameResolver, setReferer = false, signal } = params;\n const request = new Request(url, { method: 'HEAD' });\n this.#setHeaders(request, 'html', { setCookie: false, setHost: false, setReferer });\n const internalAbortController = new AbortController();\n const abortHandler = () => internalAbortController.abort();\n if (signal) {\n signal.addEventListener('abort', abortHandler, {once: true});\n }\n\n try {\n const res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n\n if (this.#assertResponseOK(res, url, request.method, false)) {\n const destFilename = destFilenameResolver.resolve(res);\n const destFilePath = path.resolve(destDir, destFilename);\n return destFilePath;\n }\n }\n finally {\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n }\n\n return undefined as never;\n }\n\n async prepareDownload<T extends Downloadable>(params: PrepareDownloadParams<T>) {\n const { url, srcEntity, destFilePath, setReferer = false, signal } = params;\n const request = new Request(url, { method: 'GET' });\n this.#setHeaders(request, 'html', { setCookie: false, setHost: false, setReferer });\n const internalAbortController = new AbortController();\n let removeAbortHandler: undefined | (() => void) = undefined;\n if (signal) {\n const abortHandler = () => internalAbortController.abort();\n signal.addEventListener('abort', abortHandler, {once: true});\n removeAbortHandler = () => signal.removeEventListener('abort', abortHandler);\n }\n\n try {\n let res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n\n // Special handling for attachment\n if (params.srcEntity.type === 'attachment' && !res.ok && res.redirected && res.url) {\n const redirectReq = new Request(res.url, { method: 'GET' });\n this.#setHeaders(redirectReq, 'html', { setCookie: false, setHost: false });\n res = await fetch(redirectReq, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n }\n\n if (this.#assertResponseOK(res, url, request.method)) {\n const destFilename = path.parse(destFilePath).base;\n const progress = new Progress(res, {\n reportInterval: 300\n });\n const monitor = new FetcherProgressMonitor(progress, destFilename, destFilePath);\n\n const _res = res;\n const start = (overrides?: StartDownloadOverrides) => {\n const _destFilePath = overrides?.destFilePath || destFilePath;\n const _tmpFilePath = overrides?.tmpFilePath || FSHelper.createTmpFilePath(_destFilePath, srcEntity.id);\n return this.#startDownload(_res, _tmpFilePath, _destFilePath, progress, removeAbortHandler)\n };\n const abort = () => {\n if (removeAbortHandler) removeAbortHandler();\n internalAbortController.abort();\n };\n\n return {\n monitor,\n start,\n abort\n };\n }\n }\n catch (error: unknown) {\n if (removeAbortHandler) removeAbortHandler();\n throw error;\n }\n\n return undefined as never;\n }\n\n async #startDownload(\n response: Response & { body: NodeJS.ReadableStream },\n tmpFilePath: string,\n destFilePath: string,\n progress: Progress,\n cleanup?: () => void) {\n\n try {\n let size = 0;\n if (this.#dryRun) {\n try {\n for await (const chunk of response.body) {\n size += chunk.length as number;\n }\n }\n catch (_error: unknown) {\n // Do nothing\n }\n }\n else {\n this.log('debug', `Pipe \"${response.url}\" to \"${tmpFilePath}\"`);\n await pipeline(\n response.body,\n progress.stream,\n fs.createWriteStream(tmpFilePath)\n );\n size = fs.lstatSync(tmpFilePath).size;\n }\n\n const commit = () => {\n this.#commitDownload(tmpFilePath, destFilePath, size, cleanup);\n };\n\n const discard = () => {\n this.#cleanupDownload(tmpFilePath, cleanup);\n };\n\n return {\n tmpFilePath,\n commit,\n discard\n };\n }\n catch (error) {\n this.#cleanupDownload(tmpFilePath, cleanup);\n throw error;\n }\n }\n\n #commitDownload(tmpFilePath: string, destFilePath: string, size: number, cleanup?: () => void) {\n try {\n this.log('debug', `Commit \"${tmpFilePath}\" to \"${destFilePath}; filesize: ${size} bytes`);\n this.#fsHelper.rename(tmpFilePath, destFilePath);\n }\n finally {\n this.#cleanupDownload(tmpFilePath, cleanup);\n }\n }\n\n #cleanupDownload(tmpFilePath: string, extra?: () => void) {\n try {\n if (this.#dryRun || fs.existsSync(tmpFilePath)) {\n this.log('debug', `Clean up \"${tmpFilePath}\"`);\n this.#fsHelper.unlink(tmpFilePath);\n }\n }\n catch (error) {\n this.log('error', `Error cleaning up \"${tmpFilePath}:`, error);\n }\n finally {\n if (extra) extra();\n }\n }\n\n #setHeaders(request: Request, type: 'html' | 'json', opts?: { setCookie?: boolean; setHost?: boolean; setReferer?: boolean; }) {\n const setCookie = pickDefined(opts?.setCookie, true);\n const setHost = pickDefined(opts?.setHost, true);\n const setReferer = pickDefined(opts?.setReferer, false);\n if (this.#cookie && setCookie) {\n request.headers.set('Cookie', this.#cookie);\n }\n if (setHost) {\n request.headers.set('Host', 'www.patreon.com');\n }\n if (setReferer) {\n request.headers.set('referer', SITE_URL);\n }\n request.headers.set('User-Agent', this.#userAgent);\n if (type === 'json') {\n request.headers.set('Content-Type', 'application/vnd.api+json');\n }\n else {\n request.headers.set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8');\n }\n }\n\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody: false): response is Response;\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody?: true): response is Response & { body: NodeJS.ReadableStream };\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody = true) {\n if (!response) {\n throw new FetcherError('No response', originURL, method);\n }\n if (!response.ok) {\n throw new FetcherError(`${response.status} - ${response.statusText}`, originURL, method);\n }\n if (requireBody && !response.body) {\n throw new FetcherError('Empty response body', originURL, method);\n }\n return true;\n }\n\n async getPageWithPuppeteer(url: string) {\n this.log('debug', `Fetch \"${url}\" with Puppeteer`);\n const browser = await puppeteer.launch({ headless: true });\n const page = await browser.newPage();\n await page.setUserAgent({ userAgent: this.#userAgent });\n await page.goto(url, { waitUntil: 'networkidle2' });\n const html = await page.evaluate(() => document.documentElement.innerHTML);\n await browser.close();\n return html;\n }\n\n protected log(level: LogLevel, ...msg: Array<any>) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n get proxyAgent() {\n return this.#proxyAgent;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Fetcher.js","sourceRoot":"","sources":["../../src/utils/Fetcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport { pipeline } from 'stream/promises';\nimport { fetch, Request, type Response } from 'undici';\nimport path from 'path';\nimport { type Dispatcher } from 'undici';\nimport puppeteer from 'puppeteer';\nimport FetcherProgressMonitor from './FetcherProgressMonitor.js';\nimport { type Downloadable } from '../entities/Downloadable.js';\nimport type FilenameResolver from './FllenameResolver.js';\nimport { pickDefined } from './Misc.js';\nimport {type LogLevel} from './logging/Logger.js';\nimport type Logger from './logging/Logger.js';\nimport { commonLog } from './logging/Logger.js';\nimport FSHelper from './FSHelper.js';\nimport { type DownloaderConfig } from '../downloaders/Downloader.js';\nimport Progress from './Progress.js';\nimport { createProxyAgent } from './Proxy.js';\nimport { SITE_URL } from './URLHelper.js';\n\nexport interface PrepareDownloadParams<T extends Downloadable> {\n url: string;\n srcEntity: T;\n destFilePath: string;\n setReferer?: boolean;\n signal: AbortSignal;\n}\n\nexport interface StartDownloadOverrides {\n destFilePath?: string;\n tmpFilePath?: string;\n}\n\nexport class FetcherError extends Error {\n\n url: string;\n method: string;\n\n constructor(message: string, url: string, method: string) {\n super(message);\n this.name = 'FetcherError';\n this.url = url;\n this.method = method;\n }\n}\n\nexport type FetcherGetType = 'html' | 'm3u8' | 'json';\nexport type FetcherGetResultOf<T extends FetcherGetType> =\n T extends 'html' ? { html: string; lastUrl: string; }:\n T extends 'm3u8' ? { contents: string; lastUrl: string; }:\n T extends 'json' ? { json: any; lastUrl: string; } :\n never;\n\nexport default class Fetcher {\n\n name = 'Fetcher';\n\n #cookie?: string;\n #logger?: Logger | null;\n #dryRun: boolean;\n #fsHelper: FSHelper;\n #proxyAgent?: Dispatcher;\n #userAgent: string;\n\n constructor(config: DownloaderConfig<any>, logger?: Logger | null) {\n this.#cookie = config.cookie;\n this.#logger = logger;\n this.#dryRun = config.dryRun;\n this.#fsHelper = new FSHelper(config, logger);\n this.#proxyAgent = createProxyAgent(config)?.agent || undefined;\n this.#userAgent = config.request.userAgent;\n }\n\n async get<T extends FetcherGetType>(args: {\n url: string,\n type: T,\n payload?: Record<string, any>,\n maxRetries: number,\n signal?: AbortSignal\n }, rt = 0): Promise<FetcherGetResultOf<T>> {\n\n const { url, type, payload, maxRetries, signal } = args;\n\n const urlObj = new URL(url);\n if (payload) {\n for (const [ p, v ] of Object.entries(payload)) {\n urlObj.searchParams.set(p, v);\n }\n }\n const request = new Request(urlObj, { method: 'GET' });\n this.#setHeaders(request, type, type === 'm3u8' ? { setReferer: true } : {});\n const internalAbortController = new AbortController();\n let removeAbortHandler: undefined | (() => void) = undefined;\n if (signal) {\n const abortHandler = () => internalAbortController.abort();\n signal.addEventListener('abort', abortHandler, {once: true});\n removeAbortHandler = () => signal.removeEventListener('abort', abortHandler);\n }\n try {\n const res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n const lastUrl = res.url;\n switch (type) {\n case 'html':\n return {\n html: await res.text(),\n lastUrl\n } as FetcherGetResultOf<T>;\n case 'm3u8':\n return {\n contents: await res.text(),\n lastUrl\n } as FetcherGetResultOf<T>;\n case 'json':\n return {\n json: await res.json(),\n lastUrl\n } as FetcherGetResultOf<T>;\n default:\n return undefined as never;\n }\n }\n catch (error: any) {\n if (signal?.aborted) {\n throw error;\n }\n if (rt < maxRetries) {\n if (removeAbortHandler) removeAbortHandler();\n return await this.get({ url, type, payload, maxRetries, signal }, rt + 1);\n }\n let errMsg;\n if (error instanceof Error) {\n const errMsgParts = [error.message];\n let _err = error.cause;\n while (_err) {\n errMsgParts.push(_err instanceof Error ? _err.message : JSON.stringify(_err));\n _err = _err instanceof Error ? _err.cause : null;\n }\n errMsg = errMsgParts.join(': ');\n }\n else {\n errMsg = String(error);\n }\n const retriedMsg = rt > 0 ? ` (retried ${rt} times)` : '';\n throw new FetcherError(`${errMsg}${retriedMsg}`, urlObj.toString(), request.method);\n }\n finally {\n if (removeAbortHandler) removeAbortHandler();\n }\n }\n\n async resolveDestFilePath<T extends Downloadable>(params: {\n url: string;\n destDir: string;\n destFilenameResolver: FilenameResolver<T>;\n setReferer?: boolean;\n signal?: AbortSignal;\n }) {\n const { url, destDir, destFilenameResolver, setReferer = false, signal } = params;\n const request = new Request(url, { method: 'HEAD' });\n this.#setHeaders(request, 'html', { setCookie: false, setHost: false, setReferer });\n const internalAbortController = new AbortController();\n const abortHandler = () => internalAbortController.abort();\n if (signal) {\n signal.addEventListener('abort', abortHandler, {once: true});\n }\n\n try {\n const res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n\n if (this.#assertResponseOK(res, url, request.method, false)) {\n const destFilename = destFilenameResolver.resolve(res);\n const destFilePath = path.resolve(destDir, destFilename);\n return destFilePath;\n }\n }\n finally {\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n }\n\n return undefined as never;\n }\n\n async prepareDownload<T extends Downloadable>(params: PrepareDownloadParams<T>) {\n const { url, srcEntity, destFilePath, setReferer = false, signal } = params;\n const request = new Request(url, { method: 'GET' });\n this.#setHeaders(request, 'html', { setCookie: false, setHost: false, setReferer });\n const internalAbortController = new AbortController();\n let removeAbortHandler: undefined | (() => void) = undefined;\n if (signal) {\n const abortHandler = () => internalAbortController.abort();\n signal.addEventListener('abort', abortHandler, {once: true});\n removeAbortHandler = () => signal.removeEventListener('abort', abortHandler);\n }\n\n try {\n let res = await fetch(request, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n\n // Special handling for attachment\n if (params.srcEntity.type === 'attachment' && !res.ok && res.redirected && res.url) {\n const redirectReq = new Request(res.url, { method: 'GET' });\n this.#setHeaders(redirectReq, 'html', { setCookie: false, setHost: false });\n res = await fetch(redirectReq, { signal: internalAbortController.signal, dispatcher: this.#proxyAgent });\n }\n\n if (this.#assertResponseOK(res, url, request.method)) {\n const destFilename = path.parse(destFilePath).base;\n const progress = new Progress(res, {\n reportInterval: 300\n });\n const monitor = new FetcherProgressMonitor(progress, destFilename, destFilePath);\n\n const _res = res;\n const start = (overrides?: StartDownloadOverrides) => {\n const _destFilePath = overrides?.destFilePath || destFilePath;\n const _tmpFilePath = overrides?.tmpFilePath || FSHelper.createTmpFilePath(_destFilePath, srcEntity.id);\n return this.#startDownload(_res, _tmpFilePath, _destFilePath, progress, removeAbortHandler)\n };\n const abort = () => {\n if (removeAbortHandler) removeAbortHandler();\n internalAbortController.abort();\n };\n\n return {\n monitor,\n start,\n abort\n };\n }\n }\n catch (error: unknown) {\n if (removeAbortHandler) removeAbortHandler();\n throw error;\n }\n\n return undefined as never;\n }\n\n async #startDownload(\n response: Response & { body: NodeJS.ReadableStream },\n tmpFilePath: string,\n destFilePath: string,\n progress: Progress,\n cleanup?: () => void) {\n\n try {\n let size = 0;\n if (this.#dryRun) {\n try {\n for await (const chunk of response.body) {\n size += chunk.length as number;\n }\n }\n catch (_error: unknown) {\n // Do nothing\n }\n }\n else {\n this.log('debug', `Pipe \"${response.url}\" to \"${tmpFilePath}\"`);\n await pipeline(\n response.body,\n progress.stream,\n fs.createWriteStream(tmpFilePath)\n );\n size = fs.lstatSync(tmpFilePath).size;\n }\n\n const commit = () => {\n this.#commitDownload(tmpFilePath, destFilePath, size, cleanup);\n };\n\n const discard = () => {\n this.#cleanupDownload(tmpFilePath, cleanup);\n };\n\n return {\n tmpFilePath,\n commit,\n discard\n };\n }\n catch (error) {\n this.#cleanupDownload(tmpFilePath, cleanup);\n throw error;\n }\n }\n\n #commitDownload(tmpFilePath: string, destFilePath: string, size: number, cleanup?: () => void) {\n try {\n this.log('debug', `Commit \"${tmpFilePath}\" to \"${destFilePath}; filesize: ${size} bytes`);\n this.#fsHelper.rename(tmpFilePath, destFilePath);\n }\n finally {\n this.#cleanupDownload(tmpFilePath, cleanup);\n }\n }\n\n #cleanupDownload(tmpFilePath: string, extra?: () => void) {\n try {\n if (this.#dryRun || fs.existsSync(tmpFilePath)) {\n this.log('debug', `Clean up \"${tmpFilePath}\"`);\n this.#fsHelper.unlink(tmpFilePath);\n }\n }\n catch (error) {\n this.log('error', `Error cleaning up \"${tmpFilePath}:`, error);\n }\n finally {\n if (extra) extra();\n }\n }\n\n #setHeaders(request: Request, type: FetcherGetType, opts?: { setCookie?: boolean; setHost?: boolean; setReferer?: boolean; }) {\n const setCookie = pickDefined(opts?.setCookie, true);\n const setHost = pickDefined(opts?.setHost, true);\n const setReferer = pickDefined(opts?.setReferer, false);\n if (this.#cookie && setCookie) {\n request.headers.set('Cookie', this.#cookie);\n }\n if (setHost) {\n request.headers.set('Host', 'www.patreon.com');\n }\n if (setReferer) {\n request.headers.set('referer', SITE_URL);\n }\n request.headers.set('User-Agent', this.#userAgent);\n if (type === 'json') {\n request.headers.set('Content-Type', 'application/vnd.api+json');\n }\n else {\n request.headers.set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8');\n }\n }\n\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody: false): response is Response;\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody?: true): response is Response & { body: NodeJS.ReadableStream };\n #assertResponseOK(response: Response | null, originURL: string, method: string, requireBody = true) {\n if (!response) {\n throw new FetcherError('No response', originURL, method);\n }\n if (!response.ok) {\n throw new FetcherError(`${response.status} - ${response.statusText}`, originURL, method);\n }\n if (requireBody && !response.body) {\n throw new FetcherError('Empty response body', originURL, method);\n }\n return true;\n }\n\n async getPageWithPuppeteer(url: string) {\n this.log('debug', `Fetch \"${url}\" with Puppeteer`);\n const browser = await puppeteer.launch({ headless: true });\n const page = await browser.newPage();\n await page.setUserAgent({ userAgent: this.#userAgent });\n await page.goto(url, { waitUntil: 'networkidle2' });\n const html = await page.evaluate(() => document.documentElement.innerHTML);\n await browser.close();\n return html;\n }\n\n protected log(level: LogLevel, ...msg: Array<any>) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n get proxyAgent() {\n return this.#proxyAgent;\n }\n}\n"]}
|