patreon-dl 1.2.0 → 1.2.2

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 CHANGED
@@ -444,7 +444,7 @@ Each event emitted by a `PatreonDownloader` instance has a payload, which is an
444
444
  | `fetchBegin` | <p>Emitted when downloader begins fetching data about target(s).<p><p>Payload properties:<ul><li>`targetType`: the type of target being fetched; one of `product`, `post` or `post`.</li></ul></p> |
445
445
  | `targetBegin` | <p>Emitted when downloader begins processing a target.</p><p>Payload properties:<ul><li>`target`: the target being processed; one of [Campaign](./docs/api/interfaces/Campaign.md), [Product](./docs/api/interfaces/Product.md) or [Post](./docs/api/interfaces/Post.md).</li></ul></p> |
446
446
  | `targetEnd` | <p>Emitted when downloader is done processing a target.</p><p>Payload properties:<ul><li>`target`: the target processed; one of [Campaign](./docs/api/interfaces/Campaign.md), [Product](./docs/api/interfaces/Product.md) or [Post](./docs/api/interfaces/Post.md).</li><li>`isSkipped`: whether target was skipped.</li></ul></p><p>If `isSkipped` is `true`, the following additional properties are available:<ul><li>`skipReason`: the reason for skipping the target; one of the following enums:<ul><li>`TargetSkipReason.Inaccessible`</li><li>`TargetSkipReason.AlreadyDownloaded`</li><li>`TargetSkipReason.UnmetMediaTypeCriteria`</li></ul></li><li>`skipMessage`: description of the skip reason.</li></ul></p> |
447
- | `phaseBegin` | <p>Emitted when downloader begins a phase in the processing of a target.</p><p>Payload properties:<ul><li>`target`: the subject target of the phase; one of [Campaign](./docs/api/interfaces/Campaign.md), [Product](./docs/api/interfaces/Product.md) or [Post](./docs/api/interfaces/Post.md).</li><li>`phase`: the phase that is about to begin; one of `saveInfo` or `batchDownload`.</li></ul></p><p>If `phase` is `batchDownload`, the following additional property is available:<ul><li>`batch`: an object representing the batch of downloads to be executed by the downloader. For monitoring downloads in the batch, see Download Task Batch.</li></ul></p> |
447
+ | `phaseBegin` | <p>Emitted when downloader begins a phase in the processing of a target.</p><p>Payload properties:<ul><li>`target`: the subject target of the phase; one of [Campaign](./docs/api/interfaces/Campaign.md), [Product](./docs/api/interfaces/Product.md) or [Post](./docs/api/interfaces/Post.md).</li><li>`phase`: the phase that is about to begin; one of `saveInfo` or `batchDownload`.</li></ul></p><p>If `phase` is `batchDownload`, the following additional property is available:<ul><li>`batch`: an object representing the batch of downloads to be executed by the downloader. For monitoring downloads in the batch, see [Download Task Batch](#download-task-batch).</li></ul></p> |
448
448
  | `phaseEnd` | <p>Emitted when a phase ends for a target.</p><p>Payload properties:<ul><li>`target`: the subject target of the phase; one of [Campaign](./docs/api/interfaces/Campaign.md), [Product](./docs/api/interfaces/Product.md) or [Post](./docs/api/interfaces/Post.md).</li><li>`phase`: the phase that has ended; one of `saveInfo` or `batchDownload`.</li></ul></p> |
449
449
  | `end` | <p>Emitted when downloader ends.</p><p>Payload properties:<ul><li>`aborted`: boolean indicating whether the downloader is ending because of an abort request</li><li>`error`: if downloader ends because of an error, then `error` will be the captured error. Note that `error` is not necessarily an `Error` object; it can be anything other than `undefined`.</li></ul></p> |
450
450
 
@@ -494,6 +494,13 @@ Each event emitted by a download task batch has a payload, which is an object wi
494
494
 
495
495
  ## Changelog
496
496
 
497
+ v1.2.2
498
+ - Fix wrong file extension for some content types
499
+ - Fix YouTube API requests throwing errors due to YT changes
500
+
501
+ v1.2.1
502
+ - Bug fixes
503
+
497
504
  v1.2.0
498
505
  - Add support for granular control over:
499
506
  - posts to include in download based on type of media contained
@@ -36,7 +36,7 @@ export default class CLIOptionValidator {
36
36
  return undefined;
37
37
  }
38
38
  const value = entry.value || undefined;
39
- const trueValues = ['yes', '1', ' true'];
39
+ const trueValues = ['yes', '1', 'true'];
40
40
  const falseValues = ['no', '0', 'false'];
41
41
  let sanitized;
42
42
  if (value) {
@@ -1 +1 @@
1
- {"version":3,"file":"CLIOptionValidator.js","sourceRoot":"","sources":["../../src/cli/CLIOptionValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { CLIOptionParserEntry } from './CLIOptions.js';\n\nconst CLI_OPTION_SRC_NAME = {\n cli: 'Command-line',\n cfg: 'Config file'\n};\n\nexport default class CLIOptionValidator {\n\n static validateRequired(entry?: CLIOptionParserEntry, errMsg?: string) {\n if (entry && entry.value) {\n return entry.value;\n }\n if (errMsg) {\n throw Error(errMsg);\n }\n if (entry) {\n throw Error(`${this.#logEntryKey(entry)} requires a value`);\n }\n throw Error('A required option missing');\n }\n\n static validateString<T extends string[]>(entry?: CLIOptionParserEntry, ...match: T): T[number] | undefined {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n if (match.length > 0 && value && !match.includes(value)) {\n throw Error(`${this.#logEntryKey(entry)} must be one of ${match.map((m) => `'${m}'`).join(', ')}`);\n }\n return value;\n }\n\n static validateBoolean(entry?: CLIOptionParserEntry) {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n const trueValues = [ 'yes', '1', ' true' ];\n const falseValues = [ 'no', '0', 'false' ];\n let sanitized: boolean | undefined;\n if (value) {\n if (trueValues.includes(value.toLowerCase())) {\n sanitized = true;\n }\n else if (falseValues.includes(value.toLowerCase())) {\n sanitized = false;\n }\n else {\n const allowedValues = [ ...trueValues, ...falseValues ];\n throw Error(`${this.#logEntryKey(entry)} must be one of ${allowedValues.map((m) => `'${m}'`).join(', ')}; currently '${value}'`);\n }\n }\n else {\n sanitized = undefined;\n }\n return sanitized;\n }\n\n static validateNumber(entry?: CLIOptionParserEntry, min?: number, max?: number) {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n const sanitized = value ? parseInt(value, 10) : undefined;\n if (sanitized !== undefined) {\n if (isNaN(sanitized)) {\n throw Error(`${this.#logEntryKey(entry)} is not a valid number`);\n }\n else if (min !== undefined && sanitized < min) {\n throw Error(`${this.#logEntryKey(entry)} must not be less than ${min}`);\n }\n else if (max !== undefined && sanitized > max) {\n throw Error(`${this.#logEntryKey(entry)} must not be greater than ${max}`);\n }\n }\n return sanitized;\n }\n\n static validateStringArray<T>(entry: CLIOptionParserEntry | undefined, match: readonly T[], delimiter = ',') {\n const value = entry?.value || undefined;\n if (!entry || !value) {\n return undefined;\n }\n const split = value.split(delimiter).reduce<string[]>((result, v) => {\n v = v.trim().toLowerCase();\n if (v && !result.includes(v)) {\n result.push(v);\n }\n return result;\n }, []);\n if (split.length === 0) {\n return undefined;\n }\n for (const v of split) {\n if (!match.includes(v as any)) {\n throw Error(`${this.#logEntryKey(entry)} has invalid delimited value '${v}'; must be one of ${match.map((m) => `'${m}'`).join(', ')}.`);\n }\n }\n return split as T[];\n }\n\n static validateIncludeContentWithMediaType(entry?: CLIOptionParserEntry) {\n try {\n return this.validateString(entry, 'any', 'none');\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio', 'attachment' ] as const);\n }\n }\n\n static validateIncludePreviewMedia(entry?: CLIOptionParserEntry) {\n try {\n return this.validateBoolean(entry);\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio' ] as const);\n }\n }\n\n static validateIncludeContentMedia(entry?: CLIOptionParserEntry) {\n try {\n return this.validateBoolean(entry);\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio', 'attachment', 'file' ] as const);\n }\n }\n\n static #logEntryKey(entry: CLIOptionParserEntry) {\n const keyStr = entry.src === 'cli' ?\n entry.key :\n `[${entry.section}]->${entry.key}`;\n return `${CLI_OPTION_SRC_NAME[entry.src]} option '${keyStr}'`;\n }\n}\n"]}
1
+ {"version":3,"file":"CLIOptionValidator.js","sourceRoot":"","sources":["../../src/cli/CLIOptionValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { CLIOptionParserEntry } from './CLIOptions.js';\n\nconst CLI_OPTION_SRC_NAME = {\n cli: 'Command-line',\n cfg: 'Config file'\n};\n\nexport default class CLIOptionValidator {\n\n static validateRequired(entry?: CLIOptionParserEntry, errMsg?: string) {\n if (entry && entry.value) {\n return entry.value;\n }\n if (errMsg) {\n throw Error(errMsg);\n }\n if (entry) {\n throw Error(`${this.#logEntryKey(entry)} requires a value`);\n }\n throw Error('A required option missing');\n }\n\n static validateString<T extends string[]>(entry?: CLIOptionParserEntry, ...match: T): T[number] | undefined {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n if (match.length > 0 && value && !match.includes(value)) {\n throw Error(`${this.#logEntryKey(entry)} must be one of ${match.map((m) => `'${m}'`).join(', ')}`);\n }\n return value;\n }\n\n static validateBoolean(entry?: CLIOptionParserEntry) {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n const trueValues = [ 'yes', '1', 'true' ];\n const falseValues = [ 'no', '0', 'false' ];\n let sanitized: boolean | undefined;\n if (value) {\n if (trueValues.includes(value.toLowerCase())) {\n sanitized = true;\n }\n else if (falseValues.includes(value.toLowerCase())) {\n sanitized = false;\n }\n else {\n const allowedValues = [ ...trueValues, ...falseValues ];\n throw Error(`${this.#logEntryKey(entry)} must be one of ${allowedValues.map((m) => `'${m}'`).join(', ')}; currently '${value}'`);\n }\n }\n else {\n sanitized = undefined;\n }\n return sanitized;\n }\n\n static validateNumber(entry?: CLIOptionParserEntry, min?: number, max?: number) {\n if (!entry) {\n return undefined;\n }\n const value = entry.value || undefined;\n const sanitized = value ? parseInt(value, 10) : undefined;\n if (sanitized !== undefined) {\n if (isNaN(sanitized)) {\n throw Error(`${this.#logEntryKey(entry)} is not a valid number`);\n }\n else if (min !== undefined && sanitized < min) {\n throw Error(`${this.#logEntryKey(entry)} must not be less than ${min}`);\n }\n else if (max !== undefined && sanitized > max) {\n throw Error(`${this.#logEntryKey(entry)} must not be greater than ${max}`);\n }\n }\n return sanitized;\n }\n\n static validateStringArray<T>(entry: CLIOptionParserEntry | undefined, match: readonly T[], delimiter = ',') {\n const value = entry?.value || undefined;\n if (!entry || !value) {\n return undefined;\n }\n const split = value.split(delimiter).reduce<string[]>((result, v) => {\n v = v.trim().toLowerCase();\n if (v && !result.includes(v)) {\n result.push(v);\n }\n return result;\n }, []);\n if (split.length === 0) {\n return undefined;\n }\n for (const v of split) {\n if (!match.includes(v as any)) {\n throw Error(`${this.#logEntryKey(entry)} has invalid delimited value '${v}'; must be one of ${match.map((m) => `'${m}'`).join(', ')}.`);\n }\n }\n return split as T[];\n }\n\n static validateIncludeContentWithMediaType(entry?: CLIOptionParserEntry) {\n try {\n return this.validateString(entry, 'any', 'none');\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio', 'attachment' ] as const);\n }\n }\n\n static validateIncludePreviewMedia(entry?: CLIOptionParserEntry) {\n try {\n return this.validateBoolean(entry);\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio' ] as const);\n }\n }\n\n static validateIncludeContentMedia(entry?: CLIOptionParserEntry) {\n try {\n return this.validateBoolean(entry);\n }\n catch (error) {\n return this.validateStringArray(entry, [ 'image', 'video', 'audio', 'attachment', 'file' ] as const);\n }\n }\n\n static #logEntryKey(entry: CLIOptionParserEntry) {\n const keyStr = entry.src === 'cli' ?\n entry.key :\n `[${entry.section}]->${entry.key}`;\n return `${CLI_OPTION_SRC_NAME[entry.src]} option '${keyStr}'`;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"StatusCache.d.ts","sourceRoot":"","sources":["../../../src/downloaders/cache/StatusCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,MAAM,EAAE,EAAE,QAAQ,EAAa,MAAM,+BAA+B,CAAC;AAM5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI;IACxD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE;QAClB,OAAO,EAAE;YACP,aAAa,EAAE,OAAO,CAAC;YACvB,WAAW,EAAE,OAAO,CAAC;YACrB,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC;YAC7D,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC;YAC7D,gBAAgB,EAAE,OAAO,CAAC;SAC3B,CAAC;KACH,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;CAC1C;AAKD,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,IACxD;IACE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;CACb,GAAG,CACF,CAAC,SAAS,IAAI,GAAG;IACf,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5B,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;CACzB,GACD,CAAC,SAAS,OAAO,GAAG;IAClB,YAAY,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC;CACjC,GACD,EAAE,CACH,CAAA;AAEH,MAAM,CAAC,OAAO,OAAO,WAAW;;IAE9B,IAAI,EAAE,aAAa,CAAC;gBAUR,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO;IA2BvF,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,UAAO;IAcjF,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC;IAoI5F,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC;IA2ChI,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE;CAI7C"}
1
+ {"version":3,"file":"StatusCache.d.ts","sourceRoot":"","sources":["../../../src/downloaders/cache/StatusCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,MAAM,EAAE,EAAE,QAAQ,EAAa,MAAM,+BAA+B,CAAC;AAM5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI;IACxD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB,EAAE;QAClB,OAAO,EAAE;YACP,aAAa,EAAE,OAAO,CAAC;YACvB,WAAW,EAAE,OAAO,CAAC;YACrB,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC;YAC7D,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,CAAC;YAC7D,gBAAgB,EAAE,OAAO,CAAC;SAC3B,CAAC;KACH,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;CAC1C;AAKD,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,IACxD;IACE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;CACb,GAAG,CACF,CAAC,SAAS,IAAI,GAAG;IACf,UAAU,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5B,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;CACzB,GACD,CAAC,SAAS,OAAO,GAAG;IAClB,YAAY,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC;CACjC,GACD,EAAE,CACH,CAAA;AAEH,MAAM,CAAC,OAAO,OAAO,WAAW;;IAE9B,IAAI,EAAE,aAAa,CAAC;gBAUR,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO;IA2BvF,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,UAAO;IAcjF,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC;IAiJ5F,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC;IA2ChI,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE;CAI7C"}
@@ -118,6 +118,18 @@ export default class StatusCache {
118
118
  const notInlast = now.filter((v) => !last.includes(v));
119
119
  invalidated = notInlast.length > 0;
120
120
  }
121
+ else if (Array.isArray(last) && now === true) {
122
+ const nowEntries = prop === 'contentMedia' ? ['image', 'video', 'audio', 'attachment', 'file'] :
123
+ prop === 'previewMedia' ? ['image', 'video', 'audio'] :
124
+ null;
125
+ if (nowEntries) {
126
+ const notInlast = nowEntries.filter((v) => !last.includes(v));
127
+ invalidated = notInlast.length > 0;
128
+ }
129
+ else {
130
+ invalidated = true;
131
+ }
132
+ }
121
133
  if (invalidated) {
122
134
  this.log('debug', `-> Invalidated: downloader config 'include.${prop}' has changed from '${JSON.stringify(last)}' -> '${JSON.stringify(now)}'`);
123
135
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StatusCache.js","sourceRoot":"","sources":["../../../src/downloaders/cache/StatusCache.ts"],"names":[],"mappings":"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","sourcesContent":["import fse from 'fs-extra';\nimport { Post } from '../../entities/Post.js';\nimport { major as semverMajor, minor as semverMinor } from 'semver';\nimport dateFormat from 'dateformat';\nimport { Product } from '../../entities/Product.js';\nimport Logger, { LogLevel, commonLog } from '../../utils/logging/Logger.js';\nimport ObjectHelper from '../../utils/ObjectHelper.js';\nimport ProductDownloader from '../ProductDownloader.js';\nimport PostDownloader from '../PostDownloader.js';\nimport path from 'path';\nimport FSHelper from '../../utils/FSHelper.js';\nimport { DownloaderConfig } from '../Downloader.js';\n\nexport interface StatusCacheData {\n products: Record<string, StatusCacheEntry<Product>>;\n posts: Record<string, StatusCacheEntry<Post>>;\n}\n\nexport interface StatusCacheEntry<T extends Product | Post> {\n type: T['type'];\n downloaderVersion: string;\n lastDownloaded: string;\n lastDownloadHasErrors: boolean;\n lastDownloadConfig: {\n include: {\n lockedContent: boolean;\n contentInfo: boolean;\n previewMedia: DownloaderConfig<T>['include']['previewMedia'];\n contentMedia: DownloaderConfig<T>['include']['contentMedia'];\n allMediaVariants: boolean;\n };\n };\n lastDestDir: string;\n lastTargetInfo: StatusCacheTargetInfo<T>;\n}\n\nconst STATUS_CACHE_FILENAME = 'status-cache.json';\nconst INSTANCE_CACHE_SIZE = 5;\n\nexport type StatusCacheTargetInfo<T extends Product | Post> =\n {\n id: T['id'];\n } & (\n T extends Post ? {\n isViewable: T['isViewable'];\n editedAt: T['editedAt'];\n } :\n T extends Product ? {\n isAccessible: T['isAccessible'];\n } :\n {}\n )\n\nexport default class StatusCache {\n\n name: 'StatusCache';\n\n static #instances: StatusCache[] = [];\n\n #dir: string;\n #file: string;\n #data: StatusCacheData;\n #logger?: Logger | null;\n #enabled: boolean;\n\n constructor(statusCacheDir: string, logger: Logger | null | undefined, enabled: boolean) {\n this.#logger = logger;\n\n const file = path.resolve(statusCacheDir, STATUS_CACHE_FILENAME);\n let data: StatusCacheData;\n if (fse.existsSync(file)) {\n data = fse.readJSONSync(file);\n this.log('debug', `Loaded status cache file \"${file}\"`);\n }\n else {\n FSHelper.createDir(statusCacheDir);\n this.log('debug', `\"${file}\" does not exist. Start with empty data`);\n data = {\n products: {},\n posts: {}\n };\n }\n if (!enabled) {\n this.log('debug', 'Status cache entry validation disabled');\n }\n\n this.#dir = statusCacheDir;\n this.#file = file;\n this.#data = data;\n this.#enabled = enabled;\n }\n\n static getInstance(statusCacheDir: string, logger?: Logger | null, enabled = true) {\n const cachedInstance = this.#instances.find(\n (sc) => sc.#dir === statusCacheDir && sc.#logger === logger && sc.#enabled === enabled);\n if (cachedInstance) {\n return cachedInstance;\n }\n const instance = new StatusCache(statusCacheDir, logger, enabled);\n this.#instances.push(instance);\n if (this.#instances.length > INSTANCE_CACHE_SIZE) {\n this.#instances.shift();\n }\n return instance;\n }\n\n validate<T extends Product | Post>(target: T, destDir: string, config: DownloaderConfig<any>) {\n if (!this.#enabled) {\n return false;\n }\n\n const entry =\n target.type === 'product' ? this.#getCacheEntry(target.id, 'product') :\n target.type === 'post' ? this.#getCacheEntry(target.id, 'post') :\n null;\n\n if (!entry) {\n this.log('debug', `Status cache entry does not exist for ${target.type} #${target.id}`);\n return false;\n }\n\n this.log('debug', `Validate status cache entry for ${target.type} #${target.id}; last downloaded: ${entry.lastDownloaded}; dest dir: \"${entry.lastDestDir}\"`);\n\n if (!entry.lastDestDir) {\n this.log('debug', '-> Invalidated: \\'lastDestDir\\' missing in status cache entry');\n return false;\n }\n // `entry.lastDestDir` is relative to the dir of status cache. Resolve to absolute.\n const lastDestDir = path.resolve(this.#dir, entry.lastDestDir);\n if (lastDestDir !== destDir) {\n this.log('debug', `-> Invalidated: destination directory has changed from \"${lastDestDir}\" to \"${destDir}\"`);\n return false;\n }\n if (!fse.existsSync(lastDestDir)) {\n this.log('debug', `-> Invalidated: destination directory \"${lastDestDir}\" does not exist`);\n return false;\n }\n\n const downloaderVer =\n target.type === 'product' ? ProductDownloader.version :\n target.type === 'post' ? PostDownloader.version :\n null;\n if (downloaderVer && !this.#validateByDownloaderVer(entry.downloaderVersion, downloaderVer)) {\n this.log('debug', '-> Invalidated: downloader version has changed (major / minor)');\n return false;\n }\n\n if (entry.lastDownloadHasErrors) {\n this.log('debug', '-> Invalidated: last download has errors');\n return false;\n }\n\n if (!entry.lastDownloadConfig?.include) {\n this.log('debug', '-> Invalidated: \\'lastDownloadConfig.include\\' missing in status cache entry');\n return false;\n }\n\n const __compareConfigInclude = (prop: keyof StatusCacheEntry<T>['lastDownloadConfig']['include'] & keyof DownloaderConfig<T>['include']) => {\n const last = entry.lastDownloadConfig.include[prop];\n const now = config.include[prop];\n\n let invalidated = false;\n // Only invalidate if 'now' scope is wider\n if (!last && now !== false) { // 'false' -> something truthy ('true' or array of values)\n invalidated = true;\n }\n else if (Array.isArray(last) && Array.isArray(now)) {\n // 'now' scope is wider if it contains entries not present in 'last'\n const notInlast = now.filter((v) => !last.includes(v as any));\n invalidated = notInlast.length > 0;\n }\n\n if (invalidated) {\n this.log('debug', `-> Invalidated: downloader config 'include.${prop}' has changed from '${JSON.stringify(last)}' -> '${JSON.stringify(now)}'`);\n }\n\n return !invalidated;\n };\n\n const includeProps: Array<keyof StatusCacheEntry<T>['lastDownloadConfig']['include']> =\n [ 'lockedContent', 'contentInfo', 'previewMedia', 'contentMedia', 'allMediaVariants' ];\n for (const prop of includeProps) {\n if (!__compareConfigInclude(prop)) {\n return false;\n }\n }\n\n if (!entry.lastTargetInfo) {\n this.log('debug', '-> Invalidated: \\'lastTargetInfo\\' missing in status cache entry');\n return false;\n }\n\n if (target.type === 'post' && entry.type === 'post' && !this.#validatePostCacheEntry(entry, target)) {\n return false;\n }\n if (target.type === 'product' && entry.type === 'product' && !this.#validateProductCacheEntry(entry, target)) {\n return false;\n }\n\n this.log('debug', '-> Validated');\n return true;\n }\n\n #validatePostCacheEntry(entry: StatusCacheEntry<Post>, post: Post) {\n if (entry.lastTargetInfo.isViewable !== post.isViewable) {\n this.log('debug', `-> Invalidated: viewability has changed (before: ${entry.lastTargetInfo.isViewable}; now: ${post.isViewable})`);\n return false;\n }\n if (entry.lastTargetInfo.editedAt !== post.editedAt) {\n this.log('debug', `-> Invalidated: post has been edited (before: ${entry.lastTargetInfo.editedAt}; now: ${post.editedAt})`);\n return false;\n }\n return true;\n }\n\n #validateProductCacheEntry(entry: StatusCacheEntry<Product>, product: Product) {\n if (entry.lastTargetInfo.isAccessible !== product.isAccessible) {\n this.log('debug', `-> Invalidated: product accessibility has changed (before: ${entry.lastTargetInfo.isAccessible}; now: ${product.isAccessible})`);\n return false;\n }\n return true;\n }\n\n #getCacheEntry(targetId: string, targetType: 'post'): StatusCacheEntry<Post> | null;\n #getCacheEntry(targetId: string, targetType: 'product'): StatusCacheEntry<Product> | null;\n #getCacheEntry(targetId: string, targetType: 'product' | 'post') {\n const key = targetType === 'product' ? `products.${targetId}` : `posts.${targetId}`;\n return ObjectHelper.getProperty(this.#data, key) || null;\n }\n\n #validateByDownloaderVer(entryValue: string, currentValue: string) {\n if (!entryValue) {\n return false;\n }\n return semverMajor(entryValue) === semverMajor(currentValue) &&\n semverMinor(entryValue) === semverMinor(currentValue);\n }\n\n updateOnDownload<T extends Product | Post>(target: T, destDir: string, downloadHasErrors: boolean, config: DownloaderConfig<any>) {\n const commonData = {\n lastDownloaded: dateFormat('isoDateTime'),\n lastDownloadHasErrors: downloadHasErrors,\n // Convert `destDir` to path relative to status cache dir\n lastDestDir: path.relative(this.#dir, destDir),\n lastDownloadConfig: {\n include: {\n lockedContent: config.include.lockedContent,\n contentInfo: config.include.contentInfo,\n previewMedia: config.include.previewMedia,\n contentMedia: config.include.contentMedia,\n allMediaVariants: config.include.allMediaVariants\n }\n }\n };\n if (target.type === 'product') {\n this.#data.products[target.id] = {\n type: 'product',\n downloaderVersion: ProductDownloader.version,\n ...commonData,\n lastTargetInfo: {\n id: target.id,\n isAccessible: target.isAccessible\n }\n };\n }\n else if (target.type === 'post') {\n this.#data.posts[target.id] = {\n type: 'post',\n downloaderVersion: PostDownloader.version,\n ...commonData,\n lastTargetInfo: {\n id: target.id,\n isViewable: target.isViewable,\n editedAt: target.editedAt\n }\n };\n }\n this.log('debug', `Update status cache for ${target.type} #${target.id}`);\n fse.writeJsonSync(this.#file, this.#data, { spaces: 2 });\n }\n\n protected log(level: LogLevel, ...msg: any[]) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n}\n"]}
1
+ {"version":3,"file":"StatusCache.js","sourceRoot":"","sources":["../../../src/downloaders/cache/StatusCache.ts"],"names":[],"mappings":"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","sourcesContent":["import fse from 'fs-extra';\nimport { Post } from '../../entities/Post.js';\nimport { major as semverMajor, minor as semverMinor } from 'semver';\nimport dateFormat from 'dateformat';\nimport { Product } from '../../entities/Product.js';\nimport Logger, { LogLevel, commonLog } from '../../utils/logging/Logger.js';\nimport ObjectHelper from '../../utils/ObjectHelper.js';\nimport ProductDownloader from '../ProductDownloader.js';\nimport PostDownloader from '../PostDownloader.js';\nimport path from 'path';\nimport FSHelper from '../../utils/FSHelper.js';\nimport { DownloaderConfig } from '../Downloader.js';\n\nexport interface StatusCacheData {\n products: Record<string, StatusCacheEntry<Product>>;\n posts: Record<string, StatusCacheEntry<Post>>;\n}\n\nexport interface StatusCacheEntry<T extends Product | Post> {\n type: T['type'];\n downloaderVersion: string;\n lastDownloaded: string;\n lastDownloadHasErrors: boolean;\n lastDownloadConfig: {\n include: {\n lockedContent: boolean;\n contentInfo: boolean;\n previewMedia: DownloaderConfig<T>['include']['previewMedia'];\n contentMedia: DownloaderConfig<T>['include']['contentMedia'];\n allMediaVariants: boolean;\n };\n };\n lastDestDir: string;\n lastTargetInfo: StatusCacheTargetInfo<T>;\n}\n\nconst STATUS_CACHE_FILENAME = 'status-cache.json';\nconst INSTANCE_CACHE_SIZE = 5;\n\nexport type StatusCacheTargetInfo<T extends Product | Post> =\n {\n id: T['id'];\n } & (\n T extends Post ? {\n isViewable: T['isViewable'];\n editedAt: T['editedAt'];\n } :\n T extends Product ? {\n isAccessible: T['isAccessible'];\n } :\n {}\n )\n\nexport default class StatusCache {\n\n name: 'StatusCache';\n\n static #instances: StatusCache[] = [];\n\n #dir: string;\n #file: string;\n #data: StatusCacheData;\n #logger?: Logger | null;\n #enabled: boolean;\n\n constructor(statusCacheDir: string, logger: Logger | null | undefined, enabled: boolean) {\n this.#logger = logger;\n\n const file = path.resolve(statusCacheDir, STATUS_CACHE_FILENAME);\n let data: StatusCacheData;\n if (fse.existsSync(file)) {\n data = fse.readJSONSync(file);\n this.log('debug', `Loaded status cache file \"${file}\"`);\n }\n else {\n FSHelper.createDir(statusCacheDir);\n this.log('debug', `\"${file}\" does not exist. Start with empty data`);\n data = {\n products: {},\n posts: {}\n };\n }\n if (!enabled) {\n this.log('debug', 'Status cache entry validation disabled');\n }\n\n this.#dir = statusCacheDir;\n this.#file = file;\n this.#data = data;\n this.#enabled = enabled;\n }\n\n static getInstance(statusCacheDir: string, logger?: Logger | null, enabled = true) {\n const cachedInstance = this.#instances.find(\n (sc) => sc.#dir === statusCacheDir && sc.#logger === logger && sc.#enabled === enabled);\n if (cachedInstance) {\n return cachedInstance;\n }\n const instance = new StatusCache(statusCacheDir, logger, enabled);\n this.#instances.push(instance);\n if (this.#instances.length > INSTANCE_CACHE_SIZE) {\n this.#instances.shift();\n }\n return instance;\n }\n\n validate<T extends Product | Post>(target: T, destDir: string, config: DownloaderConfig<any>) {\n if (!this.#enabled) {\n return false;\n }\n\n const entry =\n target.type === 'product' ? this.#getCacheEntry(target.id, 'product') :\n target.type === 'post' ? this.#getCacheEntry(target.id, 'post') :\n null;\n\n if (!entry) {\n this.log('debug', `Status cache entry does not exist for ${target.type} #${target.id}`);\n return false;\n }\n\n this.log('debug', `Validate status cache entry for ${target.type} #${target.id}; last downloaded: ${entry.lastDownloaded}; dest dir: \"${entry.lastDestDir}\"`);\n\n if (!entry.lastDestDir) {\n this.log('debug', '-> Invalidated: \\'lastDestDir\\' missing in status cache entry');\n return false;\n }\n // `entry.lastDestDir` is relative to the dir of status cache. Resolve to absolute.\n const lastDestDir = path.resolve(this.#dir, entry.lastDestDir);\n if (lastDestDir !== destDir) {\n this.log('debug', `-> Invalidated: destination directory has changed from \"${lastDestDir}\" to \"${destDir}\"`);\n return false;\n }\n if (!fse.existsSync(lastDestDir)) {\n this.log('debug', `-> Invalidated: destination directory \"${lastDestDir}\" does not exist`);\n return false;\n }\n\n const downloaderVer =\n target.type === 'product' ? ProductDownloader.version :\n target.type === 'post' ? PostDownloader.version :\n null;\n if (downloaderVer && !this.#validateByDownloaderVer(entry.downloaderVersion, downloaderVer)) {\n this.log('debug', '-> Invalidated: downloader version has changed (major / minor)');\n return false;\n }\n\n if (entry.lastDownloadHasErrors) {\n this.log('debug', '-> Invalidated: last download has errors');\n return false;\n }\n\n if (!entry.lastDownloadConfig?.include) {\n this.log('debug', '-> Invalidated: \\'lastDownloadConfig.include\\' missing in status cache entry');\n return false;\n }\n\n const __compareConfigInclude = (prop: keyof StatusCacheEntry<T>['lastDownloadConfig']['include'] & keyof DownloaderConfig<T>['include']) => {\n const last = entry.lastDownloadConfig.include[prop];\n const now = config.include[prop];\n\n let invalidated = false;\n // Only invalidate if 'now' scope is wider\n if (!last && now !== false) { // 'false' -> something truthy ('true' or array of values)\n invalidated = true;\n }\n else if (Array.isArray(last) && Array.isArray(now)) {\n // 'now' scope is wider if it contains entries not present in 'last'\n const notInlast = now.filter((v) => !last.includes(v as any));\n invalidated = notInlast.length > 0;\n }\n else if (Array.isArray(last) && now === true) {\n const nowEntries =\n prop === 'contentMedia' ? [ 'image', 'video', 'audio', 'attachment', 'file' ] :\n prop === 'previewMedia' ? [ 'image', 'video', 'audio' ] :\n null;\n if (nowEntries) {\n const notInlast = nowEntries.filter((v) => !last.includes(v as any));\n invalidated = notInlast.length > 0;\n }\n else {\n invalidated = true;\n }\n }\n\n if (invalidated) {\n this.log('debug', `-> Invalidated: downloader config 'include.${prop}' has changed from '${JSON.stringify(last)}' -> '${JSON.stringify(now)}'`);\n }\n\n return !invalidated;\n };\n\n const includeProps: Array<keyof StatusCacheEntry<T>['lastDownloadConfig']['include']> =\n [ 'lockedContent', 'contentInfo', 'previewMedia', 'contentMedia', 'allMediaVariants' ];\n for (const prop of includeProps) {\n if (!__compareConfigInclude(prop)) {\n return false;\n }\n }\n\n if (!entry.lastTargetInfo) {\n this.log('debug', '-> Invalidated: \\'lastTargetInfo\\' missing in status cache entry');\n return false;\n }\n\n if (target.type === 'post' && entry.type === 'post' && !this.#validatePostCacheEntry(entry, target)) {\n return false;\n }\n if (target.type === 'product' && entry.type === 'product' && !this.#validateProductCacheEntry(entry, target)) {\n return false;\n }\n\n this.log('debug', '-> Validated');\n return true;\n }\n\n #validatePostCacheEntry(entry: StatusCacheEntry<Post>, post: Post) {\n if (entry.lastTargetInfo.isViewable !== post.isViewable) {\n this.log('debug', `-> Invalidated: viewability has changed (before: ${entry.lastTargetInfo.isViewable}; now: ${post.isViewable})`);\n return false;\n }\n if (entry.lastTargetInfo.editedAt !== post.editedAt) {\n this.log('debug', `-> Invalidated: post has been edited (before: ${entry.lastTargetInfo.editedAt}; now: ${post.editedAt})`);\n return false;\n }\n return true;\n }\n\n #validateProductCacheEntry(entry: StatusCacheEntry<Product>, product: Product) {\n if (entry.lastTargetInfo.isAccessible !== product.isAccessible) {\n this.log('debug', `-> Invalidated: product accessibility has changed (before: ${entry.lastTargetInfo.isAccessible}; now: ${product.isAccessible})`);\n return false;\n }\n return true;\n }\n\n #getCacheEntry(targetId: string, targetType: 'post'): StatusCacheEntry<Post> | null;\n #getCacheEntry(targetId: string, targetType: 'product'): StatusCacheEntry<Product> | null;\n #getCacheEntry(targetId: string, targetType: 'product' | 'post') {\n const key = targetType === 'product' ? `products.${targetId}` : `posts.${targetId}`;\n return ObjectHelper.getProperty(this.#data, key) || null;\n }\n\n #validateByDownloaderVer(entryValue: string, currentValue: string) {\n if (!entryValue) {\n return false;\n }\n return semverMajor(entryValue) === semverMajor(currentValue) &&\n semverMinor(entryValue) === semverMinor(currentValue);\n }\n\n updateOnDownload<T extends Product | Post>(target: T, destDir: string, downloadHasErrors: boolean, config: DownloaderConfig<any>) {\n const commonData = {\n lastDownloaded: dateFormat('isoDateTime'),\n lastDownloadHasErrors: downloadHasErrors,\n // Convert `destDir` to path relative to status cache dir\n lastDestDir: path.relative(this.#dir, destDir),\n lastDownloadConfig: {\n include: {\n lockedContent: config.include.lockedContent,\n contentInfo: config.include.contentInfo,\n previewMedia: config.include.previewMedia,\n contentMedia: config.include.contentMedia,\n allMediaVariants: config.include.allMediaVariants\n }\n }\n };\n if (target.type === 'product') {\n this.#data.products[target.id] = {\n type: 'product',\n downloaderVersion: ProductDownloader.version,\n ...commonData,\n lastTargetInfo: {\n id: target.id,\n isAccessible: target.isAccessible\n }\n };\n }\n else if (target.type === 'post') {\n this.#data.posts[target.id] = {\n type: 'post',\n downloaderVersion: PostDownloader.version,\n ...commonData,\n lastTargetInfo: {\n id: target.id,\n isViewable: target.isViewable,\n editedAt: target.editedAt\n }\n };\n }\n this.log('debug', `Update status cache for ${target.type} #${target.id}`);\n fse.writeJsonSync(this.#file, this.#data, { spaces: 2 });\n }\n\n protected log(level: LogLevel, ...msg: any[]) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"FllenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC;;;;;;GAMG;AAEH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,gBAAgB,CAAC,CAAC;IAE9C,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACpB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEb,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM;IAKrC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAE5C,SAAS,CAAC,4BAA4B,CAAC,QAAQ,EAAE,QAAQ;;;;IA6BzD,SAAS,CAAC,yBAAyB,CAAC,IAAI,EAAE,MAAM;CAWjD"}
1
+ {"version":3,"file":"FllenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC;;;;;;GAMG;AAEH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,gBAAgB,CAAC,CAAC;IAE9C,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACpB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEb,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM;IAKrC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAE5C,SAAS,CAAC,4BAA4B,CAAC,QAAQ,EAAE,QAAQ;;;;IA6BzD,SAAS,CAAC,yBAAyB,CAAC,IAAI,EAAE,MAAM;CAuBjD"}
@@ -41,10 +41,22 @@ export default class FilenameResolver {
41
41
  return parts;
42
42
  }
43
43
  getExtensionByContentType(type) {
44
+ const undeterminable = [
45
+ 'application/octet-stream',
46
+ 'text/plain'
47
+ ];
48
+ if (undeterminable.includes(type)) {
49
+ return '';
50
+ }
51
+ const extFromURL = URLHelper.getExtensionFromURL(this.srcURL);
44
52
  // Mime-types does not recognize 'application/x-mpegURL'
45
- if (type === 'application/x-mpegURL' && URLHelper.getExtensionFromURL(this.srcURL) === '.m3u8') {
53
+ if (type === 'application/x-mpegURL' && extFromURL === '.m3u8') {
46
54
  return '.m3u8';
47
55
  }
56
+ // Mime-types returns '.mpga' for 'audio/mpeg' - we want '.mp3'
57
+ else if (type === 'audio/mpeg' && extFromURL === '.mp3') {
58
+ return '.mp3';
59
+ }
48
60
  let ext = mimeTypes.extension(type);
49
61
  if (ext === 'jpeg') {
50
62
  ext = 'jpg';
@@ -1 +1 @@
1
- {"version":3,"file":"FllenameResolver.js","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,qBAAqB,CAAC;AACrD,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAEvC;;;;;;GAMG;AAEH,MAAM,CAAC,OAAO,OAAgB,gBAAgB;IAK5C,YAAY,MAAS,EAAE,MAAc;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAIS,4BAA4B,CAAC,QAAkB;QACvD,MAAM,KAAK,GAAG;YACZ,IAAI,EAAE,EAAE;YACR,GAAG,EAAE,EAAE;SACR,CAAC;QAEF,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAChE,IAAI,WAAW,EAAE;YACf,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAChE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;YAClE,IAAI,QAAQ,EAAE;gBACZ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACpC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;gBACzB,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;aACxB;SACF;QACD,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;QACjE,IAAI,WAAW,EAAE;YACf,MAAM,gBAAgB,GAAG,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC;YACrE,IAAI,gBAAgB,EAAE;gBACpB,KAAK,CAAC,GAAG,GAAG,gBAAgB,CAAC;aAC9B;SACF;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAES,yBAAyB,CAAC,IAAY;QAC9C,wDAAwD;QACxD,IAAI,IAAI,KAAK,uBAAuB,IAAI,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO,EAAE;YAC9F,OAAO,OAAO,CAAC;SAChB;QACD,IAAI,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM,EAAE;YAClB,GAAG,GAAG,KAAK,CAAC;SACb;QACD,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,CAAC;CACF","sourcesContent":["import contentDisposition from 'content-disposition';\nimport mimeTypes from 'mime-types';\nimport { Response } from 'node-fetch';\nimport path from 'path';\nimport URLHelper from './URLHelper.js';\n\n/**\n * Used by `Fetcher.download()` to resolve the destination filename\n * of the downloaded item. The resolver implementation depends on the\n * type of target being downloaded. `resolve()` is called with the\n * `Response` object fetched by `Fetcher`, since filename resolution could\n * use data provided in the response headers.\n */\n\nexport default abstract class FilenameResolver<T> {\n\n protected target: T;\n protected srcURL: string;\n\n constructor(target: T, srcURL: string) {\n this.target = target;\n this.srcURL = srcURL;\n }\n\n abstract resolve(response: Response): string;\n\n protected getFilenamePartsFromResponse(response: Response) {\n const parts = {\n name: '',\n ext: ''\n };\n\n const disposition = response.headers.get('content-disposition');\n if (disposition) {\n const parsedDisposition = contentDisposition.parse(disposition);\n const filename = parsedDisposition.parameters['filename'] || null;\n if (filename) {\n const parsed = path.parse(filename);\n parts.name = parsed.name;\n parts.ext = parsed.ext;\n }\n }\n // Filename obtained from content-disposition could have wrong extension.\n // Always use extension derived from headers content-type if available.\n const contentType = response.headers.get('content-type') || null;\n if (contentType) {\n const extByContentType = this.getExtensionByContentType(contentType);\n if (extByContentType) {\n parts.ext = extByContentType;\n }\n }\n\n return parts;\n }\n\n protected getExtensionByContentType(type: string) {\n // Mime-types does not recognize 'application/x-mpegURL'\n if (type === 'application/x-mpegURL' && URLHelper.getExtensionFromURL(this.srcURL) === '.m3u8') {\n return '.m3u8';\n }\n let ext = mimeTypes.extension(type);\n if (ext === 'jpeg') {\n ext = 'jpg';\n }\n return ext ? `.${ext}` : '';\n }\n}\n"]}
1
+ {"version":3,"file":"FllenameResolver.js","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,qBAAqB,CAAC;AACrD,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAEvC;;;;;;GAMG;AAEH,MAAM,CAAC,OAAO,OAAgB,gBAAgB;IAK5C,YAAY,MAAS,EAAE,MAAc;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAIS,4BAA4B,CAAC,QAAkB;QACvD,MAAM,KAAK,GAAG;YACZ,IAAI,EAAE,EAAE;YACR,GAAG,EAAE,EAAE;SACR,CAAC;QAEF,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAChE,IAAI,WAAW,EAAE;YACf,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAChE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;YAClE,IAAI,QAAQ,EAAE;gBACZ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACpC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;gBACzB,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;aACxB;SACF;QACD,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;QACjE,IAAI,WAAW,EAAE;YACf,MAAM,gBAAgB,GAAG,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC;YACrE,IAAI,gBAAgB,EAAE;gBACpB,KAAK,CAAC,GAAG,GAAG,gBAAgB,CAAC;aAC9B;SACF;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAES,yBAAyB,CAAC,IAAY;QAC9C,MAAM,cAAc,GAAG;YACrB,0BAA0B;YAC1B,YAAY;SACb,CAAC;QACF,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;YACjC,OAAO,EAAE,CAAC;SACX;QACD,MAAM,UAAU,GAAG,SAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9D,wDAAwD;QACxD,IAAI,IAAI,KAAK,uBAAuB,IAAI,UAAU,KAAK,OAAO,EAAE;YAC9D,OAAO,OAAO,CAAC;SAChB;QACD,+DAA+D;aAC1D,IAAI,IAAI,KAAK,YAAY,IAAI,UAAU,KAAK,MAAM,EAAE;YACvD,OAAO,MAAM,CAAC;SACf;QACD,IAAI,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM,EAAE;YAClB,GAAG,GAAG,KAAK,CAAC;SACb;QACD,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,CAAC;CACF","sourcesContent":["import contentDisposition from 'content-disposition';\nimport mimeTypes from 'mime-types';\nimport { Response } from 'node-fetch';\nimport path from 'path';\nimport URLHelper from './URLHelper.js';\n\n/**\n * Used by `Fetcher.download()` to resolve the destination filename\n * of the downloaded item. The resolver implementation depends on the\n * type of target being downloaded. `resolve()` is called with the\n * `Response` object fetched by `Fetcher`, since filename resolution could\n * use data provided in the response headers.\n */\n\nexport default abstract class FilenameResolver<T> {\n\n protected target: T;\n protected srcURL: string;\n\n constructor(target: T, srcURL: string) {\n this.target = target;\n this.srcURL = srcURL;\n }\n\n abstract resolve(response: Response): string;\n\n protected getFilenamePartsFromResponse(response: Response) {\n const parts = {\n name: '',\n ext: ''\n };\n\n const disposition = response.headers.get('content-disposition');\n if (disposition) {\n const parsedDisposition = contentDisposition.parse(disposition);\n const filename = parsedDisposition.parameters['filename'] || null;\n if (filename) {\n const parsed = path.parse(filename);\n parts.name = parsed.name;\n parts.ext = parsed.ext;\n }\n }\n // Filename obtained from content-disposition could have wrong extension.\n // Always use extension derived from headers content-type if available.\n const contentType = response.headers.get('content-type') || null;\n if (contentType) {\n const extByContentType = this.getExtensionByContentType(contentType);\n if (extByContentType) {\n parts.ext = extByContentType;\n }\n }\n\n return parts;\n }\n\n protected getExtensionByContentType(type: string) {\n const undeterminable = [\n 'application/octet-stream',\n 'text/plain'\n ];\n if (undeterminable.includes(type)) {\n return '';\n }\n const extFromURL = URLHelper.getExtensionFromURL(this.srcURL);\n // Mime-types does not recognize 'application/x-mpegURL'\n if (type === 'application/x-mpegURL' && extFromURL === '.m3u8') {\n return '.m3u8';\n }\n // Mime-types returns '.mpga' for 'audio/mpeg' - we want '.mp3'\n else if (type === 'audio/mpeg' && extFromURL === '.mp3') {\n return '.mp3';\n }\n let ext = mimeTypes.extension(type);\n if (ext === 'jpeg') {\n ext = 'jpg';\n }\n return ext ? `.${ext}` : '';\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patreon-dl",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Patreon Downloader",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",
@@ -72,7 +72,7 @@
72
72
  "prompt-sync": "^4.2.0",
73
73
  "sanitize-filename": "^1.6.3",
74
74
  "semver": "^7.5.4",
75
- "youtubei.js": "^7.0.0"
75
+ "youtubei.js": "^9.1.0"
76
76
  },
77
77
  "keywords": [
78
78
  "patreon",