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.
@@ -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 (this.config.maxVideoResolution && this.config.maxVideoResolution > 0) {
53
- if (input.resolution) {
54
- this.log('info', `Video found with resolution at or below specified max resolution "${this.config.maxVideoResolution}": ${input.resolution}`);
55
- const { name: filename, ext, dir } = path.parse(__classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f"));
56
- output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename} (${input.resolution})${ext}`));
57
- }
58
- else {
59
- this.log('info', `No video found with resolution at or below specified max resolution "${this.config.maxVideoResolution}" - going to download highest`);
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 maxResolution = this.config.maxVideoResolution;
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: 'html',
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
- const variants = parser.manifest.playlists;
97
- this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);
98
- if (!variants) {
99
- this.log('debug', `No videos found in m3u8 manifest - going to download video with highest resolution`);
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: null
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
- const filtered = variants
111
- .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)))
112
- .sort((a, b) => b.attributes.RESOLUTION.height - a.attributes.RESOLUTION.height);
113
- if (filtered.length === 0) {
114
- this.log('debug', `No video found in m3u8 manifest at or below resolution "${maxResolution}" - going to download video with highest resolution`);
115
- return {
116
- src: this.src,
117
- resolution: null
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 = filtered[0];
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: `${selected.attributes.RESOLUTION.width}x${selected.attributes.RESOLUTION.height}`
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 = /campaign_id\\",\\"unit_id\\":\\"(.+?)\\"/gm;
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":";;;;;;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,4CAA4C,CAAC;YACrE,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 = /campaign_id\\\\\",\\\\\"unit_id\\\\\":\\\\\"(.+?)\\\\\"/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"]}
1
+ {"version":3,"file":"PageParser.js","sourceRoot":"","sources":["../../src/parsers/PageParser.ts"],"names":[],"mappings":"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","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"]}
@@ -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;
@@ -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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patreon-dl",
3
- "version": "3.7.0",
3
+ "version": "3.7.1",
4
4
  "description": "Patreon Downloader",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",