patreon-dl 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +9 -209
  2. package/dist/browse/db/CampaignDBMixin.d.ts +3 -3
  3. package/dist/browse/db/ContentDBMixin.d.ts +13 -13
  4. package/dist/browse/db/ContentDBMixin.js +1 -1
  5. package/dist/browse/db/ContentDBMixin.js.map +1 -1
  6. package/dist/browse/db/MediaDBMixin.js +1 -1
  7. package/dist/browse/db/MediaDBMixin.js.map +1 -1
  8. package/dist/browse/db/index.d.ts +16 -16
  9. package/dist/browse/server/handler/CampaignAPIRequesthandler.js +5 -0
  10. package/dist/browse/server/handler/CampaignAPIRequesthandler.js.map +1 -1
  11. package/dist/browse/web/assets/index-CtjPL2rI.js +218 -0
  12. package/dist/browse/web/index.html +1 -1
  13. package/dist/cli/CLIOptions.js +6 -2
  14. package/dist/cli/CLIOptions.js.map +1 -1
  15. package/dist/cli/ConfigFileParser.js +1 -1
  16. package/dist/cli/ConfigFileParser.js.map +1 -1
  17. package/dist/downloaders/Downloader.js +1 -1
  18. package/dist/downloaders/Downloader.js.map +1 -1
  19. package/dist/downloaders/PostDownloader.js +5 -5
  20. package/dist/downloaders/PostDownloader.js.map +1 -1
  21. package/dist/downloaders/PostsFetcher.js +3 -3
  22. package/dist/downloaders/PostsFetcher.js.map +1 -1
  23. package/dist/downloaders/task/DownloadTask.js +11 -1
  24. package/dist/downloaders/task/DownloadTask.js.map +1 -1
  25. package/dist/entities/MediaItem.d.ts +1 -0
  26. package/dist/entities/MediaItem.js.map +1 -1
  27. package/dist/parsers/PageParser.js +4 -1
  28. package/dist/parsers/PageParser.js.map +1 -1
  29. package/dist/parsers/PostParser.d.ts +4 -1
  30. package/dist/parsers/PostParser.js +82 -5
  31. package/dist/parsers/PostParser.js.map +1 -1
  32. package/dist/utils/Fetcher.d.ts +1 -0
  33. package/dist/utils/Fetcher.js +5 -0
  34. package/dist/utils/Fetcher.js.map +1 -1
  35. package/dist/utils/FilenameFormatHelper.d.ts +1 -1
  36. package/dist/utils/FilenameFormatHelper.js +4 -1
  37. package/dist/utils/FilenameFormatHelper.js.map +1 -1
  38. package/dist/utils/Formatter.d.ts +1 -1
  39. package/dist/utils/Formatter.js +1 -1
  40. package/dist/utils/Formatter.js.map +1 -1
  41. package/dist/utils/logging/FileLogger.js +1 -0
  42. package/dist/utils/logging/FileLogger.js.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/browse/web/assets/index-CtZbx-Du.js +0 -218
@@ -1 +1 @@
1
- {"version":3,"file":"DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type Downloadable } from '../../entities/Downloadable.js';\nimport {type LogLevel} from '../../utils/logging/Logger.js';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog } from '../../utils/logging/Logger.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport { type DownloaderConfig } from '../Downloader.js';\nimport type Bottleneck from 'bottleneck';\nimport {fileTypeFromFile} from 'file-type';\nimport fs from 'fs/promises';\nimport { imageSize } from 'image-size';\n\nexport class DownloadTaskError extends Error {\n task: IDownloadTask;\n cause?: Error;\n\n constructor(message: string, task: IDownloadTask, cause?: Error) {\n super(message);\n this.name = 'DownloadTaskError';\n this.task = task;\n this.cause = cause;\n }\n}\n\nexport type DownloadTaskSkipReason = {\n message: string;\n} & ({\n name: 'destFileExists';\n existingDestFilePath: string;\n} | {\n name: 'includeMediaByFilenameUnfulfilled';\n itemType: 'image' | 'audio' | 'attachment';\n pattern: string;\n destFilename: string;\n} | {\n name: 'dependentTaskNotCompleted'\n} | {\n name: 'other';\n})\n\nexport interface DownloadProgress {\n destFilename: string;\n destFilePath: string;\n lengthUnit: string;\n length?: number; // Measured in `lengthUnit`\n lengthDownloaded: number; // Measured in `lengthUnit`\n percent?: number;\n sizeDownloaded: number; // Kb\n speed: number; // Kb/s\n}\n\nexport interface DownloadTaskParams<T extends Downloadable = Downloadable> {\n src: string;\n srcEntity: T,\n config: DownloaderConfig<any>;\n downloadType: 'main' | 'thumbnail' | 'variant';\n callbacks: DownloadTaskCallbacks | null;\n logger?: Logger | null;\n}\n\nexport interface DownloadTaskCallbacks {\n onStart: (task: DownloadTask) => void;\n onProgress: (task: DownloadTask, progress: DownloadProgress | null) => void;\n onComplete: (task: DownloadTask) => void;\n onAbort: (task: DownloadTask) => void;\n onSkip: (task: DownloadTask, reason: DownloadTaskSkipReason) => void;\n onError: (error: DownloadTaskError, willRetry: boolean) => void;\n onSpawn: (origin: DownloadTask, spawn: DownloadTask) => void;\n}\n\nexport type DownloadTaskStatus =\n 'pending' |\n 'pending-retry' | // Pending retry on error\n 'downloading' |\n 'error' |\n 'completed' |\n 'aborted' |\n 'skipped';\n\nconst ENDED_STATUSES: DownloadTaskStatus[] = [\n 'error',\n 'completed',\n 'aborted',\n 'skipped'\n];\n\nexport interface IDownloadTask {\n id: number;\n src: string;\n srcEntity: Downloadable;\n retryCount: number;\n resolvedDestFilename: string | null;\n resolvedDestPath: string | null;\n getProgress: () => DownloadProgress | null;\n}\n\nexport default abstract class DownloadTask<T extends Downloadable = Downloadable> implements IDownloadTask {\n\n protected abstract name: string;\n\n static #idCounter = 0;\n\n #id: number;\n #config: DownloaderConfig<any>;\n #src: string;\n #retryCount: number;\n #resolvedDestPath: string | null;\n #srcEntity: T;\n #downloadType: 'main' | 'thumbnail' | 'variant';\n #callbacks: DownloadTaskCallbacks | null;\n #logger?: Logger | null;\n #lastProgress: DownloadProgress | null;\n #status: DownloadTaskStatus;\n #fsHelper: FSHelper;\n #doa: {\n msg: string;\n cause: unknown;\n } | null;\n\n #startPromise: Promise<void> | null;\n #startingCallback: (() => void) | null;\n #abortPromise: Promise<void> | null;\n\n constructor(params: DownloadTaskParams<T>) {\n this.#id = DownloadTask.#idCounter;\n this.#config = params.config;\n this.#src = params.src;\n this.#retryCount = 0;\n this.#resolvedDestPath = null;\n this.#srcEntity = params.srcEntity;\n this.#downloadType = params.downloadType;\n this.#callbacks = params.callbacks;\n this.#logger = params.logger;\n this.#lastProgress = null;\n this.#status = 'pending';\n this.#doa = null;\n this.#startPromise = null;\n this.#startingCallback = null;\n this.#abortPromise = null;\n\n this.#fsHelper = new FSHelper(params.config, params.logger);\n\n DownloadTask.#idCounter++;\n }\n\n protected abstract resolveDestPath(signal?: AbortSignal): Promise<string>;\n protected abstract doStart(): Promise<void>;\n protected abstract doAbort(): Promise<void>;\n protected abstract doDestroy(): Promise<void>;\n protected abstract doGetProgress(): DownloadProgress | null | undefined;\n\n static async create<A extends DownloadTask<B>, B extends Downloadable, C extends DownloadTaskParams<B>>(\n classname: new (p: C) => A,\n params: C,\n limiter?: Bottleneck,\n signal?: AbortSignal\n ): Promise<A> {\n const task = new classname(params);\n const maxRetries = params.config.request.maxRetries;\n const __doResolveDestPath = async () => {\n let t = 0;\n let err: unknown;\n while (t < maxRetries + 1) {\n try {\n const p = await task.resolveDestPath(signal);\n task.resolvedDestPath = p;\n task.log('debug', `(Task create #${task.srcEntity.id}) Resolved dest path: \"${p}\"`);\n return {\n hasError: false,\n destPath: p\n };\n }\n catch (error: unknown) {\n if (signal?.aborted) {\n throw error;\n }\n const willRetry = t < maxRetries ? 'will retry' : 'max reached';\n const retryStr = t > 0 ? `retried ${t} times - ${willRetry}`: willRetry;\n task.log('error', `(Task create #${task.srcEntity.id}) Error resolving dest path (${retryStr}):`, error);\n err = error;\n t++;\n }\n }\n return {\n hasError: true,\n msg: 'Could not resolve destination path',\n error: err\n };\n };\n const result = limiter ? await limiter.schedule(() => __doResolveDestPath()) : await __doResolveDestPath();\n if (result.hasError) {\n task.#doa = {\n msg: result.msg as string,\n cause: result.error\n };\n }\n return task;\n }\n\n addSuffixToDestPath(suffix: string) {\n if (!this.resolvedDestPath) {\n throw Error('Destination file path not resolved');\n }\n const parts = path.parse(this.resolvedDestPath);\n const newFilename = FSHelper.createFilename({\n name: parts.name,\n suffix,\n ext: parts.ext\n });\n this.resolvedDestPath = path.resolve(parts.dir, newFilename);\n }\n\n start() {\n if (this.#startPromise) {\n return this.#startPromise;\n }\n\n if (this.hasEnded()) {\n // Cannot start an ended task\n return Promise.resolve();\n }\n\n if (this.#doa) {\n this.notifyError(Error(this.#doa.msg, { cause: this.#doa.cause }), false);\n return Promise.resolve();\n }\n\n this.#startPromise = new Promise<void>((resolve) => {\n void (async () => {\n this.#startingCallback = () => {\n this.#startPromise = null;\n this.#startingCallback = null;\n resolve();\n };\n \n await this.doStart();\n \n // Retry on error, up to `maxRetries`\n while (this.#status === 'pending-retry' && this.#retryCount < this.maxRetries) {\n this.#retryCount++;\n await this.doStart();\n }\n })();\n });\n\n return this.#startPromise;\n }\n\n abort() {\n if (this.#abortPromise) {\n return this.#abortPromise;\n }\n\n this.#abortPromise = new Promise<void>((resolve) => {\n void (async () => {\n if (this.isRunning()) {\n await this.doAbort();\n }\n else if (this.#status === 'pending' || this.#status === 'pending-retry') {\n this.notifyAbort();\n }\n resolve();\n })();\n })\n .finally(() => {\n this.#abortPromise = null;\n });\n\n return this.#abortPromise;\n }\n\n getProgress() {\n const progress = this.doGetProgress();\n if (progress === undefined) {\n return this.#lastProgress;\n }\n this.#lastProgress = progress;\n return progress;\n }\n\n protected notifyStart() {\n this.#status = 'downloading';\n this.#lastProgress = this.getProgress();\n if (this.callbacks?.onStart) {\n this.callbacks.onStart(this);\n }\n }\n\n protected notifyProgress(progress: DownloadProgress | null) {\n this.#lastProgress = progress;\n if (this.callbacks?.onProgress) {\n this.callbacks.onProgress(this, progress);\n }\n }\n\n protected notifyComplete() {\n this.#notifyEnd('completed');\n }\n\n protected notifyAbort() {\n this.#notifyEnd('aborted');\n }\n\n protected notifySkip(reason: DownloadTaskSkipReason) {\n this.#notifyEnd('skipped', reason);\n }\n\n protected notifyError(error: any, allowRetry = true) {\n const msg = error instanceof Error ? error.message : error;\n const err = new DownloadTaskError(msg, this);\n if (error instanceof Error) {\n err.cause = error;\n }\n const willRetry = allowRetry && this.#retryCount < this.maxRetries;\n this.#notifyEnd('error', err, willRetry);\n }\n\n #notifyEnd(status: 'error', err: DownloadTaskError, willRetry: boolean): void;\n #notifyEnd(status: 'skipped', reason: DownloadTaskSkipReason): void;\n #notifyEnd(status: 'aborted'): void;\n #notifyEnd(status: 'completed'): void;\n #notifyEnd(status: 'completed' | 'aborted' | 'skipped' | 'error', ...cbPayload: any[]) {\n this.#status = status;\n let clearCallbacks = true;\n let callStartingCallback = true;\n switch (status) {\n case 'completed':\n if (this.callbacks?.onComplete) {\n this.callbacks.onComplete(this);\n }\n break;\n case 'aborted':\n if (this.callbacks?.onAbort) {\n this.callbacks.onAbort(this);\n }\n break;\n case 'skipped':\n if (this.callbacks?.onSkip) {\n const [ reason ] = cbPayload;\n this.callbacks.onSkip(this, reason);\n }\n break;\n case 'error': {\n const [ err, willRetry ] = cbPayload;\n if (willRetry) {\n this.#status = 'pending-retry';\n clearCallbacks = false;\n callStartingCallback = false;\n }\n if (this.callbacks?.onError) {\n this.callbacks.onError(err, willRetry);\n }\n }\n }\n\n if (this.#startingCallback && callStartingCallback) {\n this.#startingCallback();\n }\n\n if (clearCallbacks) {\n this.#callbacks = null;\n }\n }\n\n isRunning() {\n return this.status === 'downloading';\n }\n\n isAborting() {\n return !!this.#abortPromise;\n }\n\n hasEnded() {\n return ENDED_STATUSES.includes(this.#status);\n }\n\n async setDownloaded(filePath: string) {\n if (this.dryRun || !['main', 'thumbnail'].includes(this.downloadType)) {\n return;\n }\n if (!this.#srcEntity.downloaded) {\n this.#srcEntity.downloaded = {};\n }\n const rel = path.relative(this.#config.outDir, filePath);\n // Replace '\\' with '/' - for cross-platform interoperability\n const normalized = rel.replace(/\\\\/g, '/');\n if (this.downloadType === 'main') {\n this.#srcEntity.downloaded.mimeType = (await fileTypeFromFile(filePath))?.mime?.toLowerCase() || null;\n this.#srcEntity.downloaded.path = normalized;\n }\n else if (this.downloadType === 'thumbnail') {\n if (!this.#srcEntity.downloaded.thumbnail) {\n this.#srcEntity.downloaded.thumbnail = {};\n }\n this.#srcEntity.downloaded.thumbnail.mimeType = (await fileTypeFromFile(filePath))?.mime.toLowerCase() || null;\n this.#srcEntity.downloaded.thumbnail.path = normalized;\n let size;\n try {\n const buffer = await fs.readFile(filePath);\n size = imageSize(buffer);\n }\n catch (_error) {\n size = null;\n }\n if (size) {\n this.#srcEntity.downloaded.thumbnail.width = size.width;\n this.#srcEntity.downloaded.thumbnail.height = size.height;\n }\n }\n }\n\n get id() {\n return this.#id;\n }\n\n set id(value: number) {\n this.#id = value;\n }\n\n get src() {\n return this.#src;\n }\n\n get resolvedDestFilename() {\n return this.#resolvedDestPath ? path.basename(this.#resolvedDestPath) : null;\n }\n\n get resolvedDestPath() {\n return this.#resolvedDestPath;\n }\n\n get doa() {\n return this.#doa;\n }\n\n protected set resolvedDestPath(value: string | null) {\n this.#resolvedDestPath = value;\n }\n\n get srcEntity() {\n return this.#srcEntity;\n }\n\n get downloadType() {\n return this.#downloadType;\n }\n\n set downloadType(value: 'main' | 'thumbnail' | 'variant') {\n this.#downloadType = value;\n }\n\n protected get config() {\n return this.#config;\n }\n\n protected get maxRetries() {\n return this.#config.request.maxRetries;\n }\n\n protected get dryRun() {\n return this.#config.dryRun;\n }\n\n get retryCount() {\n return this.#retryCount;\n }\n\n get status() {\n return this.#status;\n }\n\n protected set status(value: DownloadTaskStatus) {\n this.#status = value;\n }\n\n protected get callbacks() {\n return this.#callbacks;\n }\n\n protected get fsHelper() {\n return this.#fsHelper;\n }\n\n setCallbacks(value: DownloadTaskCallbacks | null) {\n if (this.status !== 'pending') {\n throw Error('You can only set callbacks of a pending task');\n }\n this.#callbacks = value;\n }\n\n protected log(level: LogLevel, ...msg: Array<any>) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n protected get logger() {\n return this.#logger;\n }\n}\n"]}
1
+ {"version":3,"file":"DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type Downloadable } from '../../entities/Downloadable.js';\nimport {type LogLevel} from '../../utils/logging/Logger.js';\nimport type Logger from '../../utils/logging/Logger.js';\nimport { commonLog } from '../../utils/logging/Logger.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport { type DownloaderConfig } from '../Downloader.js';\nimport type Bottleneck from 'bottleneck';\nimport {fileTypeFromFile} from 'file-type';\nimport fs from 'fs/promises';\nimport { imageSize } from 'image-size';\nimport Sleeper from '../../utils/Sleeper.js';\n\nexport class DownloadTaskError extends Error {\n task: IDownloadTask;\n cause?: Error;\n\n constructor(message: string, task: IDownloadTask, cause?: Error) {\n super(message);\n this.name = 'DownloadTaskError';\n this.task = task;\n this.cause = cause;\n }\n}\n\nexport type DownloadTaskSkipReason = {\n message: string;\n} & ({\n name: 'destFileExists';\n existingDestFilePath: string;\n} | {\n name: 'includeMediaByFilenameUnfulfilled';\n itemType: 'image' | 'audio' | 'attachment';\n pattern: string;\n destFilename: string;\n} | {\n name: 'dependentTaskNotCompleted'\n} | {\n name: 'other';\n})\n\nexport interface DownloadProgress {\n destFilename: string;\n destFilePath: string;\n lengthUnit: string;\n length?: number; // Measured in `lengthUnit`\n lengthDownloaded: number; // Measured in `lengthUnit`\n percent?: number;\n sizeDownloaded: number; // Kb\n speed: number; // Kb/s\n}\n\nexport interface DownloadTaskParams<T extends Downloadable = Downloadable> {\n src: string;\n srcEntity: T,\n config: DownloaderConfig<any>;\n downloadType: 'main' | 'thumbnail' | 'variant';\n callbacks: DownloadTaskCallbacks | null;\n logger?: Logger | null;\n}\n\nexport interface DownloadTaskCallbacks {\n onStart: (task: DownloadTask) => void;\n onProgress: (task: DownloadTask, progress: DownloadProgress | null) => void;\n onComplete: (task: DownloadTask) => void;\n onAbort: (task: DownloadTask) => void;\n onSkip: (task: DownloadTask, reason: DownloadTaskSkipReason) => void;\n onError: (error: DownloadTaskError, willRetry: boolean) => void;\n onSpawn: (origin: DownloadTask, spawn: DownloadTask) => void;\n}\n\nexport type DownloadTaskStatus =\n 'pending' |\n 'pending-retry' | // Pending retry on error\n 'downloading' |\n 'error' |\n 'completed' |\n 'aborted' |\n 'skipped';\n\nconst ENDED_STATUSES: DownloadTaskStatus[] = [\n 'error',\n 'completed',\n 'aborted',\n 'skipped'\n];\n\nexport interface IDownloadTask {\n id: number;\n src: string;\n srcEntity: Downloadable;\n retryCount: number;\n resolvedDestFilename: string | null;\n resolvedDestPath: string | null;\n getProgress: () => DownloadProgress | null;\n}\n\nexport default abstract class DownloadTask<T extends Downloadable = Downloadable> implements IDownloadTask {\n\n protected abstract name: string;\n\n static #idCounter = 0;\n\n #id: number;\n #config: DownloaderConfig<any>;\n #src: string;\n #retryCount: number;\n #resolvedDestPath: string | null;\n #srcEntity: T;\n #downloadType: 'main' | 'thumbnail' | 'variant';\n #callbacks: DownloadTaskCallbacks | null;\n #logger?: Logger | null;\n #lastProgress: DownloadProgress | null;\n #status: DownloadTaskStatus;\n #fsHelper: FSHelper;\n #doa: {\n msg: string;\n cause: unknown;\n } | null;\n\n #startPromise: Promise<void> | null;\n #startingCallback: (() => void) | null;\n #abortPromise: Promise<void> | null;\n\n constructor(params: DownloadTaskParams<T>) {\n this.#id = DownloadTask.#idCounter;\n this.#config = params.config;\n this.#src = params.src;\n this.#retryCount = 0;\n this.#resolvedDestPath = null;\n this.#srcEntity = params.srcEntity;\n this.#downloadType = params.downloadType;\n this.#callbacks = params.callbacks;\n this.#logger = params.logger;\n this.#lastProgress = null;\n this.#status = 'pending';\n this.#doa = null;\n this.#startPromise = null;\n this.#startingCallback = null;\n this.#abortPromise = null;\n\n this.#fsHelper = new FSHelper(params.config, params.logger);\n\n DownloadTask.#idCounter++;\n }\n\n protected abstract resolveDestPath(signal?: AbortSignal): Promise<string>;\n protected abstract doStart(): Promise<void>;\n protected abstract doAbort(): Promise<void>;\n protected abstract doDestroy(): Promise<void>;\n protected abstract doGetProgress(): DownloadProgress | null | undefined;\n\n static async create<A extends DownloadTask<B>, B extends Downloadable, C extends DownloadTaskParams<B>>(\n classname: new (p: C) => A,\n params: C,\n limiter?: Bottleneck,\n signal?: AbortSignal\n ): Promise<A> {\n const task = new classname(params);\n const maxRetries = params.config.request.maxRetries;\n const __doResolveDestPath = async () => {\n let t = 0;\n let err: unknown;\n while (t < maxRetries + 1) {\n try {\n const p = await task.resolveDestPath(signal);\n task.resolvedDestPath = p;\n task.log('debug', `(Task create #${task.srcEntity.id}) Resolved dest path: \"${p}\"`);\n return {\n hasError: false,\n destPath: p\n };\n }\n catch (error: unknown) {\n if (signal?.aborted) {\n throw error;\n }\n const willRetry = t < maxRetries ? `will retry in ${1000 * t}ms` : 'max reached';\n const retryStr = t > 0 ? `retried ${t} times - ${willRetry}`: willRetry;\n task.log('error', `(Task create #${task.srcEntity.id}) Error resolving dest path (${retryStr}):`, error);\n err = error;\n t++;\n if (t < maxRetries + 1) {\n const sleeper = Sleeper.getInstance(1000 * t, signal);\n try {\n await sleeper.start();\n }\n finally {\n sleeper.destroy();\n }\n }\n }\n }\n return {\n hasError: true,\n msg: 'Could not resolve destination path',\n error: err\n };\n };\n const result = limiter ? await limiter.schedule(() => __doResolveDestPath()) : await __doResolveDestPath();\n if (result.hasError) {\n task.#doa = {\n msg: result.msg as string,\n cause: result.error\n };\n }\n return task;\n }\n\n addSuffixToDestPath(suffix: string) {\n if (!this.resolvedDestPath) {\n throw Error('Destination file path not resolved');\n }\n const parts = path.parse(this.resolvedDestPath);\n const newFilename = FSHelper.createFilename({\n name: parts.name,\n suffix,\n ext: parts.ext\n });\n this.resolvedDestPath = path.resolve(parts.dir, newFilename);\n }\n\n start() {\n if (this.#startPromise) {\n return this.#startPromise;\n }\n\n if (this.hasEnded()) {\n // Cannot start an ended task\n return Promise.resolve();\n }\n\n if (this.#doa) {\n this.notifyError(Error(this.#doa.msg, { cause: this.#doa.cause }), false);\n return Promise.resolve();\n }\n\n this.#startPromise = new Promise<void>((resolve) => {\n void (async () => {\n this.#startingCallback = () => {\n this.#startPromise = null;\n this.#startingCallback = null;\n resolve();\n };\n \n await this.doStart();\n \n // Retry on error, up to `maxRetries`\n while (this.#status === 'pending-retry' && this.#retryCount < this.maxRetries) {\n this.#retryCount++;\n await this.doStart();\n }\n })();\n });\n\n return this.#startPromise;\n }\n\n abort() {\n if (this.#abortPromise) {\n return this.#abortPromise;\n }\n\n this.#abortPromise = new Promise<void>((resolve) => {\n void (async () => {\n if (this.isRunning()) {\n await this.doAbort();\n }\n else if (this.#status === 'pending' || this.#status === 'pending-retry') {\n this.notifyAbort();\n }\n resolve();\n })();\n })\n .finally(() => {\n this.#abortPromise = null;\n });\n\n return this.#abortPromise;\n }\n\n getProgress() {\n const progress = this.doGetProgress();\n if (progress === undefined) {\n return this.#lastProgress;\n }\n this.#lastProgress = progress;\n return progress;\n }\n\n protected notifyStart() {\n this.#status = 'downloading';\n this.#lastProgress = this.getProgress();\n if (this.callbacks?.onStart) {\n this.callbacks.onStart(this);\n }\n }\n\n protected notifyProgress(progress: DownloadProgress | null) {\n this.#lastProgress = progress;\n if (this.callbacks?.onProgress) {\n this.callbacks.onProgress(this, progress);\n }\n }\n\n protected notifyComplete() {\n this.#notifyEnd('completed');\n }\n\n protected notifyAbort() {\n this.#notifyEnd('aborted');\n }\n\n protected notifySkip(reason: DownloadTaskSkipReason) {\n this.#notifyEnd('skipped', reason);\n }\n\n protected notifyError(error: any, allowRetry = true) {\n const msg = error instanceof Error ? error.message : error;\n const err = new DownloadTaskError(msg, this);\n if (error instanceof Error) {\n err.cause = error;\n }\n const willRetry = allowRetry && this.#retryCount < this.maxRetries;\n this.#notifyEnd('error', err, willRetry);\n }\n\n #notifyEnd(status: 'error', err: DownloadTaskError, willRetry: boolean): void;\n #notifyEnd(status: 'skipped', reason: DownloadTaskSkipReason): void;\n #notifyEnd(status: 'aborted'): void;\n #notifyEnd(status: 'completed'): void;\n #notifyEnd(status: 'completed' | 'aborted' | 'skipped' | 'error', ...cbPayload: any[]) {\n this.#status = status;\n let clearCallbacks = true;\n let callStartingCallback = true;\n switch (status) {\n case 'completed':\n if (this.callbacks?.onComplete) {\n this.callbacks.onComplete(this);\n }\n break;\n case 'aborted':\n if (this.callbacks?.onAbort) {\n this.callbacks.onAbort(this);\n }\n break;\n case 'skipped':\n if (this.callbacks?.onSkip) {\n const [ reason ] = cbPayload;\n this.callbacks.onSkip(this, reason);\n }\n break;\n case 'error': {\n const [ err, willRetry ] = cbPayload;\n if (willRetry) {\n this.#status = 'pending-retry';\n clearCallbacks = false;\n callStartingCallback = false;\n }\n if (this.callbacks?.onError) {\n this.callbacks.onError(err, willRetry);\n }\n }\n }\n\n if (this.#startingCallback && callStartingCallback) {\n this.#startingCallback();\n }\n\n if (clearCallbacks) {\n this.#callbacks = null;\n }\n }\n\n isRunning() {\n return this.status === 'downloading';\n }\n\n isAborting() {\n return !!this.#abortPromise;\n }\n\n hasEnded() {\n return ENDED_STATUSES.includes(this.#status);\n }\n\n async setDownloaded(filePath: string) {\n if (this.dryRun || !['main', 'thumbnail'].includes(this.downloadType)) {\n return;\n }\n if (!this.#srcEntity.downloaded) {\n this.#srcEntity.downloaded = {};\n }\n const rel = path.relative(this.#config.outDir, filePath);\n // Replace '\\' with '/' - for cross-platform interoperability\n const normalized = rel.replace(/\\\\/g, '/');\n if (this.downloadType === 'main') {\n this.#srcEntity.downloaded.mimeType = (await fileTypeFromFile(filePath))?.mime?.toLowerCase() || null;\n this.#srcEntity.downloaded.path = normalized;\n }\n else if (this.downloadType === 'thumbnail') {\n if (!this.#srcEntity.downloaded.thumbnail) {\n this.#srcEntity.downloaded.thumbnail = {};\n }\n this.#srcEntity.downloaded.thumbnail.mimeType = (await fileTypeFromFile(filePath))?.mime.toLowerCase() || null;\n this.#srcEntity.downloaded.thumbnail.path = normalized;\n let size;\n try {\n const buffer = await fs.readFile(filePath);\n size = imageSize(buffer);\n }\n catch (_error) {\n size = null;\n }\n if (size) {\n this.#srcEntity.downloaded.thumbnail.width = size.width;\n this.#srcEntity.downloaded.thumbnail.height = size.height;\n }\n }\n }\n\n get id() {\n return this.#id;\n }\n\n set id(value: number) {\n this.#id = value;\n }\n\n get src() {\n return this.#src;\n }\n\n get resolvedDestFilename() {\n return this.#resolvedDestPath ? path.basename(this.#resolvedDestPath) : null;\n }\n\n get resolvedDestPath() {\n return this.#resolvedDestPath;\n }\n\n get doa() {\n return this.#doa;\n }\n\n protected set resolvedDestPath(value: string | null) {\n this.#resolvedDestPath = value;\n }\n\n get srcEntity() {\n return this.#srcEntity;\n }\n\n get downloadType() {\n return this.#downloadType;\n }\n\n set downloadType(value: 'main' | 'thumbnail' | 'variant') {\n this.#downloadType = value;\n }\n\n protected get config() {\n return this.#config;\n }\n\n protected get maxRetries() {\n return this.#config.request.maxRetries;\n }\n\n protected get dryRun() {\n return this.#config.dryRun;\n }\n\n get retryCount() {\n return this.#retryCount;\n }\n\n get status() {\n return this.#status;\n }\n\n protected set status(value: DownloadTaskStatus) {\n this.#status = value;\n }\n\n protected get callbacks() {\n return this.#callbacks;\n }\n\n protected get fsHelper() {\n return this.#fsHelper;\n }\n\n setCallbacks(value: DownloadTaskCallbacks | null) {\n if (this.status !== 'pending') {\n throw Error('You can only set callbacks of a pending task');\n }\n this.#callbacks = value;\n }\n\n protected log(level: LogLevel, ...msg: Array<any>) {\n commonLog(this.#logger, level, this.name, ...msg);\n }\n\n protected get logger() {\n return this.#logger;\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  export interface MediaLike {
2
2
  type: string;
3
3
  id: string;
4
+ index?: number;
4
5
  filename: string | null;
5
6
  mimeType: string | null;
6
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MediaItem.js","sourceRoot":"","sources":["../../src/entities/MediaItem.ts"],"names":[],"mappings":"","sourcesContent":["export interface MediaLike {\n type: string;\n id: string;\n filename: string | null;\n mimeType: string | null;\n}\n\nexport type ImageType =\n 'single' |\n 'default' |\n 'campaignCoverPhoto' |\n 'postCoverImage' |\n 'postThumbnail' |\n 'collectionThumbnail';\n\nexport interface SingleImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'single';\n imageURL: string | null;\n thumbnailURL: string | null;\n}\n\nexport interface DefaultImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'default';\n createdAt: string | null;\n downloadURL: string | null;\n imageURLs: {\n default: string | null;\n defaultSmall: string | null;\n original: string | null; // This is not the same as downloadURL (which has smaller filesize)\n thumbnail: string | null;\n thumbnailLarge: string | null;\n thumbnailSmall: string | null;\n };\n thumbnailURL: string | null;\n}\n\nexport interface CampaignCoverPhotoMediaItem extends MediaLike {\n type: 'image';\n imageType: 'campaignCoverPhoto';\n imageURLs: {\n large: string | null;\n medium: string | null;\n small: string | null;\n xlarge: string | null;\n xsmall: string | null;\n }\n}\n\nexport interface PostCoverImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'postCoverImage';\n imageURLs: {\n large: string | null;\n thumbSquareLarge: string | null;\n thumbSquare: string | null;\n thumb: string | null;\n default: string | null;\n }\n}\n\nexport interface PostThumbnailMediaItem extends MediaLike {\n type: 'image';\n imageType: 'postThumbnail';\n imageURLs: {\n large: string | null;\n large2: string | null;\n square: string | null;\n default: string | null;\n }\n}\n\nexport interface CollectionThumbnailMediaItem extends MediaLike {\n type: 'image';\n imageType: 'collectionThumbnail';\n imageURLs: {\n url: string | null;\n original: string | null;\n default: string | null;\n defaultBlurred: string | null;\n defaultSmall: string | null;\n defaultLarge: string | null;\n thumbnail: string | null;\n thumbnailLarge: string | null;\n thumbnailSmall: string | null;\n };\n}\n\nexport type ImageMediaItem<T extends ImageType> =\n T extends 'single' ? SingleImageMediaItem :\n T extends 'default' ? DefaultImageMediaItem :\n T extends 'campaignCoverPhoto' ? CampaignCoverPhotoMediaItem :\n T extends 'postCoverImage' ? PostCoverImageMediaItem :\n T extends 'postThumbnail' ? PostThumbnailMediaItem :\n T extends 'collectionThumbnail' ? CollectionThumbnailMediaItem :\n never;\n\nexport interface VideoMediaItem extends MediaLike {\n type: 'video';\n createdAt: string | null;\n size: {\n width: number | null;\n height: number | null;\n },\n duration: number | null; // Seconds\n downloadURL: string | null;\n displayURL: string | null; // URL of video displayed on-screen\n thumbnailURL: string | null;\n}\n\nexport interface AudioMediaItem extends MediaLike {\n type: 'audio';\n createdAt: string | null;\n url: string | null;\n thumbnailURL: string | null;\n}\n\nexport interface FileMediaItem extends MediaLike {\n type: 'file';\n createdAt: string | null;\n downloadURL: string | null;\n}\n\nexport interface AttachmentMediaItem extends MediaLike {\n type: 'attachment';\n downloadURL: string | null;\n}\n\n/**\n * Minimal `MediaItem` typically used to represent media-type properties of elements, such\n * as video thumbnails and campaign avatar / cover photos.\n * As a `MediaItem` type, and hence also a `Downloadable` type, it can be used to create\n * `MediaFilenameResolver` and `DownloadTask` instances.\n */\nexport interface DummyMediaItem extends MediaLike {\n type: 'dummy';\n // { variant: url }\n srcURLs: Record<string, string | null>;\n}\n\nexport type MediaItem =\n ImageMediaItem<any> |\n VideoMediaItem |\n AudioMediaItem |\n FileMediaItem |\n AttachmentMediaItem |\n DummyMediaItem;\n"]}
1
+ {"version":3,"file":"MediaItem.js","sourceRoot":"","sources":["../../src/entities/MediaItem.ts"],"names":[],"mappings":"","sourcesContent":["export interface MediaLike {\n type: string;\n id: string;\n index?: number;\n filename: string | null;\n mimeType: string | null;\n}\n\nexport type ImageType =\n 'single' |\n 'default' |\n 'campaignCoverPhoto' |\n 'postCoverImage' |\n 'postThumbnail' |\n 'collectionThumbnail';\n\nexport interface SingleImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'single';\n imageURL: string | null;\n thumbnailURL: string | null;\n}\n\nexport interface DefaultImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'default';\n createdAt: string | null;\n downloadURL: string | null;\n imageURLs: {\n default: string | null;\n defaultSmall: string | null;\n original: string | null; // This is not the same as downloadURL (which has smaller filesize)\n thumbnail: string | null;\n thumbnailLarge: string | null;\n thumbnailSmall: string | null;\n };\n thumbnailURL: string | null;\n}\n\nexport interface CampaignCoverPhotoMediaItem extends MediaLike {\n type: 'image';\n imageType: 'campaignCoverPhoto';\n imageURLs: {\n large: string | null;\n medium: string | null;\n small: string | null;\n xlarge: string | null;\n xsmall: string | null;\n }\n}\n\nexport interface PostCoverImageMediaItem extends MediaLike {\n type: 'image';\n imageType: 'postCoverImage';\n imageURLs: {\n large: string | null;\n thumbSquareLarge: string | null;\n thumbSquare: string | null;\n thumb: string | null;\n default: string | null;\n }\n}\n\nexport interface PostThumbnailMediaItem extends MediaLike {\n type: 'image';\n imageType: 'postThumbnail';\n imageURLs: {\n large: string | null;\n large2: string | null;\n square: string | null;\n default: string | null;\n }\n}\n\nexport interface CollectionThumbnailMediaItem extends MediaLike {\n type: 'image';\n imageType: 'collectionThumbnail';\n imageURLs: {\n url: string | null;\n original: string | null;\n default: string | null;\n defaultBlurred: string | null;\n defaultSmall: string | null;\n defaultLarge: string | null;\n thumbnail: string | null;\n thumbnailLarge: string | null;\n thumbnailSmall: string | null;\n };\n}\n\nexport type ImageMediaItem<T extends ImageType> =\n T extends 'single' ? SingleImageMediaItem :\n T extends 'default' ? DefaultImageMediaItem :\n T extends 'campaignCoverPhoto' ? CampaignCoverPhotoMediaItem :\n T extends 'postCoverImage' ? PostCoverImageMediaItem :\n T extends 'postThumbnail' ? PostThumbnailMediaItem :\n T extends 'collectionThumbnail' ? CollectionThumbnailMediaItem :\n never;\n\nexport interface VideoMediaItem extends MediaLike {\n type: 'video';\n createdAt: string | null;\n size: {\n width: number | null;\n height: number | null;\n },\n duration: number | null; // Seconds\n downloadURL: string | null;\n displayURL: string | null; // URL of video displayed on-screen\n thumbnailURL: string | null;\n}\n\nexport interface AudioMediaItem extends MediaLike {\n type: 'audio';\n createdAt: string | null;\n url: string | null;\n thumbnailURL: string | null;\n}\n\nexport interface FileMediaItem extends MediaLike {\n type: 'file';\n createdAt: string | null;\n downloadURL: string | null;\n}\n\nexport interface AttachmentMediaItem extends MediaLike {\n type: 'attachment';\n downloadURL: string | null;\n}\n\n/**\n * Minimal `MediaItem` typically used to represent media-type properties of elements, such\n * as video thumbnails and campaign avatar / cover photos.\n * As a `MediaItem` type, and hence also a `Downloadable` type, it can be used to create\n * `MediaFilenameResolver` and `DownloadTask` instances.\n */\nexport interface DummyMediaItem extends MediaLike {\n type: 'dummy';\n // { variant: url }\n srcURLs: Record<string, string | null>;\n}\n\nexport type MediaItem =\n ImageMediaItem<any> |\n VideoMediaItem |\n AudioMediaItem |\n FileMediaItem |\n AttachmentMediaItem |\n DummyMediaItem;\n"]}
@@ -15,7 +15,10 @@ class PageParser extends Parser {
15
15
  parseInitialData(html, _url) {
16
16
  this.log('debug', `Parse initial data from ${_url}`);
17
17
  const initialDataRegex = /window\.patreon\s*?=\s*?({.+?});/gm;
18
- const initialDataRegex2 = /<script id="__NEXT_DATA__" type="application\/json">(.+)<\/script>/gm;
18
+ // Allow optional attributes in <script> tag.
19
+ // Credit: hmv47 (https://github.com/hmv47)
20
+ // https://github.com/patrickkfkan/patreon-dl/issues/134
21
+ const initialDataRegex2 = /<script id="__NEXT_DATA__" type="application\/json"[^>]*>(.+)<\/script>/gm;
19
22
  this.log('debug', `Trying pattern: ${initialDataRegex}`);
20
23
  const match = initialDataRegex.exec(html);
21
24
  if (match && match[1]) {
@@ -1 +1 @@
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 let campaignIdMatch = campaignIdRegex.exec(html);\n\n if (!campaignIdMatch || !campaignIdMatch[1]) {\n const campaignIdRegexSimple = /{\\\\\"self\\\\\":\\\\\"https:\\/\\/[^\"]+\\/api\\/campaigns\\/(.+?)\\\\\"}/gm;\n this.log('debug', `Trying fallback pattern: ${campaignIdRegexSimple}`);\n campaignIdMatch = campaignIdRegexSimple.exec(html);\n }\n\n if (!campaignIdMatch || !campaignIdMatch[1]) {\n throw Error(`Initial data not found - campaign ID not found in Next.js streaming response`);\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 // Allow optional attributes in <script> tag.\n // Credit: hmv47 (https://github.com/hmv47)\n // https://github.com/patrickkfkan/patreon-dl/issues/134\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 let campaignIdMatch = campaignIdRegex.exec(html);\n\n if (!campaignIdMatch || !campaignIdMatch[1]) {\n const campaignIdRegexSimple = /{\\\\\"self\\\\\":\\\\\"https:\\/\\/[^\"]+\\/api\\/campaigns\\/(.+?)\\\\\"}/gm;\n this.log('debug', `Trying fallback pattern: ${campaignIdRegexSimple}`);\n campaignIdMatch = campaignIdRegexSimple.exec(html);\n }\n\n if (!campaignIdMatch || !campaignIdMatch[1]) {\n throw Error(`Initial data not found - campaign ID not found in Next.js streaming response`);\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,7 +1,10 @@
1
1
  import { type PostList as PostList } from '../entities/Post.js';
2
2
  import Parser from './Parser.js';
3
+ import type Logger from '../utils/logging/Logger.js';
4
+ import type Fetcher from '../utils/Fetcher.js';
3
5
  export default class PostParser extends Parser {
4
6
  #private;
5
7
  protected name: string;
6
- parsePostsAPIResponse(json: any, src: string): PostList;
8
+ constructor(fetcher: Fetcher, logger?: Logger | null);
9
+ parsePostsAPIResponse(json: any, src: string): Promise<PostList>;
7
10
  }
@@ -1,22 +1,31 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
1
7
  var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
8
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
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");
4
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
11
  };
6
- var _PostParser_instances, _PostParser_parseContent, _PostParser_getVideoMediaItemFromAttr;
12
+ var _PostParser_instances, _PostParser_fetcher, _PostParser_parseContent, _PostParser_getVideoMediaItemFromAttr;
7
13
  import { stripHtml } from 'string-strip-html';
14
+ import { isYouTubeEmbed } from '../entities/Downloadable.js';
8
15
  import { PostType } from '../entities/Post.js';
9
16
  import { pickDefined } from '../utils/Misc.js';
10
17
  import ObjectHelper from '../utils/ObjectHelper.js';
11
18
  import Parser from './Parser.js';
12
19
  import URLHelper from '../utils/URLHelper.js';
13
20
  class PostParser extends Parser {
14
- constructor() {
15
- super(...arguments);
21
+ constructor(fetcher, logger) {
22
+ super(logger);
16
23
  _PostParser_instances.add(this);
17
24
  this.name = 'PostParser';
25
+ _PostParser_fetcher.set(this, void 0);
26
+ __classPrivateFieldSet(this, _PostParser_fetcher, fetcher, "f");
18
27
  }
19
- parsePostsAPIResponse(json, src) {
28
+ async parsePostsAPIResponse(json, src) {
20
29
  this.log('debug', `Parse API data obtained from "${src}"`);
21
30
  /*If (json.errors) {
22
31
  this.log('error', `API response error:`, json.errors);
@@ -108,6 +117,31 @@ class PostParser extends Parser {
108
117
  images = downloadables.images || [];
109
118
  attachments = downloadables.attachments_media || [];
110
119
  }
120
+ // If post metadata has image_order, use that to arrange images
121
+ const imageOrder = ObjectHelper.getProperty(attributes, 'post_metadata.image_order');
122
+ if (Array.isArray(imageOrder) && images.length > 0) {
123
+ const orderedImages = [];
124
+ for (const imageId of imageOrder) {
125
+ const imgIndex = images.findIndex((i) => i.id === imageId);
126
+ if (imgIndex !== -1) {
127
+ orderedImages.push(images[imgIndex]);
128
+ images.splice(imgIndex, 1);
129
+ }
130
+ else {
131
+ this.log('warn', `Image with ID "${imageId}" listed in 'image_order' metadata of post #${id} not found among images obtained from relationships - skipping`);
132
+ }
133
+ }
134
+ if (images.length > 0) {
135
+ this.log('warn', `${images.length} images obtained from relationships of post #${id} not listed in 'image_order' metadata - appending at the end`);
136
+ orderedImages.push(...images);
137
+ }
138
+ images = orderedImages.map((img, index) => ({ ...img, index }));
139
+ }
140
+ else if (images.length > 0) {
141
+ images.forEach((img, index) => {
142
+ img.index = index;
143
+ });
144
+ }
111
145
  // Post content and teaser
112
146
  const content = __classPrivateFieldGet(this, _PostParser_instances, "m", _PostParser_parseContent).call(this, attributes);
113
147
  const teaserText = __classPrivateFieldGet(this, _PostParser_instances, "m", _PostParser_parseContent).call(this, {
@@ -119,6 +153,9 @@ class PostParser extends Parser {
119
153
  const inlineMediaBody = content || teaserText;
120
154
  if (hasIncludedJSON && inlineMediaBody) {
121
155
  const inlineMedia = this.parseInlineMedia(id, inlineMediaBody, includedJSON);
156
+ inlineMedia.images.forEach((img, index) => {
157
+ img.index = images.length + index;
158
+ });
122
159
  images.push(...inlineMedia.images);
123
160
  linkedAttachments.push(...inlineMedia.linkedAttachments);
124
161
  }
@@ -225,6 +262,46 @@ class PostParser extends Parser {
225
262
  thumbnailURL: ObjectHelper.getProperty(attributes, 'image.large_url') || null // Use cover image
226
263
  };
227
264
  }
265
+ // YouTube embeds can have 0-byte thumbnail:
266
+ // https://github.com/patrickkfkan/patreon-dl/issues/120
267
+ if (embed && isYouTubeEmbed(embed) && embed.url) {
268
+ // Credit: @Fabelwesen (https://github.com/Fabelwesen)
269
+ const ytMatch = embed.url.match(/(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([a-zA-Z0-9_-]{11})/);
270
+ if (ytMatch) {
271
+ const ytId = ytMatch[1];
272
+ // Use maxresdefault for best quality
273
+ const ytThumbnailURL = `https://img.youtube.com/vi/${ytId}/maxresdefault.jpg`;
274
+ if (await __classPrivateFieldGet(this, _PostParser_fetcher, "f").test(ytThumbnailURL)) {
275
+ this.log('debug', `Set YouTube embed thumbnail URL to "${ytThumbnailURL}"`);
276
+ const originalThumbnailURL = embed.thumbnailURL;
277
+ embed.thumbnailURL = ytThumbnailURL;
278
+ const __replace = (o) => {
279
+ Object.keys(o.imageURLs).forEach((key) => {
280
+ if (o?.imageURLs[key] === originalThumbnailURL) {
281
+ o.imageURLs[key] = ytThumbnailURL;
282
+ }
283
+ });
284
+ };
285
+ if (coverImage) {
286
+ __replace(coverImage);
287
+ }
288
+ if (thumbnail) {
289
+ __replace(thumbnail);
290
+ }
291
+ if (images) {
292
+ for (const img of images) {
293
+ __replace(img);
294
+ if (img.thumbnailURL === originalThumbnailURL) {
295
+ img.thumbnailURL = ytThumbnailURL;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ else {
301
+ this.log('debug', `Test failed for YouTube thumbnail URL "${ytThumbnailURL}". Going to use URL from API data.`);
302
+ }
303
+ }
304
+ }
228
305
  // Tiers
229
306
  let tiers = [];
230
307
  const tierData = ObjectHelper.getProperty(postJSON, 'relationships.access_rules.data');
@@ -320,7 +397,7 @@ class PostParser extends Parser {
320
397
  }
321
398
  ;
322
399
  }
323
- _PostParser_instances = new WeakSet(), _PostParser_parseContent = function _PostParser_parseContent(data) {
400
+ _PostParser_fetcher = new WeakMap(), _PostParser_instances = new WeakSet(), _PostParser_parseContent = function _PostParser_parseContent(data) {
324
401
  const { content, content_json_string } = data;
325
402
  if (typeof content === 'string' && content.trim()) {
326
403
  return content;
@@ -1 +1 @@
1
- {"version":3,"file":"PostParser.js","sourceRoot":"","sources":["../../src/parsers/PostParser.ts"],"names":[],"mappings":"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","sourcesContent":["import { stripHtml } from 'string-strip-html';\nimport { type Campaign } from '../entities/Campaign.js';\nimport { type Downloadable } from '../entities/Downloadable.js';\nimport { type AttachmentMediaItem, type AudioMediaItem, type DefaultImageMediaItem, type MediaItem, type PostCoverImageMediaItem, type PostThumbnailMediaItem, type VideoMediaItem } from '../entities/MediaItem.js';\nimport { type LinkedAttachment, PostType, type Post, type PostList as PostList, type PostEmbed, type Collection, type PostTag } from '../entities/Post.js';\nimport { type Tier } from '../entities/Reward.js';\nimport { pickDefined } from '../utils/Misc.js';\nimport ObjectHelper from '../utils/ObjectHelper.js';\nimport Parser from './Parser.js';\nimport URLHelper from '../utils/URLHelper.js';\n\nexport default class PostParser extends Parser {\n\n protected name = 'PostParser';\n\n parsePostsAPIResponse(json: any, src: string): PostList {\n\n this.log('debug', `Parse API data obtained from \"${src}\"`);\n\n /*If (json.errors) {\n this.log('error', `API response error:`, json.errors);\n return null;\n }*/\n\n const includedJSON = json.included;\n const dataJSON = json.data;\n let postsJSONArray: any[];\n // Check if API data consists of just a single post (not an array).\n // If so, place the post data in an array.\n if (dataJSON && !Array.isArray(dataJSON) && dataJSON.type === 'post') {\n postsJSONArray = [ dataJSON ];\n }\n // If API data is an array, filter out those matching 'post' type.\n else if (dataJSON && Array.isArray(dataJSON)) {\n postsJSONArray = dataJSON.filter((data) => data.type === 'post');\n }\n else {\n // No posts found\n postsJSONArray = [];\n }\n const collection: PostList = {\n items: [],\n total: ObjectHelper.getProperty(json, 'meta.pagination.total') || null,\n // src could be a file, in which case there'll be no nextURL.\n nextURL: URLHelper.validateURL(src) ? this.parseCollectionNextURL(json, src) : null\n };\n\n let hasIncludedJSON = true;\n if (!includedJSON || !Array.isArray(includedJSON)) {\n this.log('warn', `'included' field missing in API data obtained from \"${src}\" or has incorrect type - no media items and campaign info will be returned`);\n hasIncludedJSON = false;\n }\n\n if (postsJSONArray.length === 0) {\n this.log('warn', `No posts found in API data obtained from \"${src}\"`);\n return collection;\n }\n if (postsJSONArray.length > 1) {\n this.log('debug', `${postsJSONArray.length} posts found - iterate and parse`);\n }\n else {\n this.log('debug', '1 post found - parse');\n }\n\n let campaign: Campaign | null | undefined;\n\n for (const postJSON of postsJSONArray) {\n if (!postJSON || typeof postJSON !== 'object') {\n this.log('error', 'Parse error: API data of post has incorrect type');\n continue;\n }\n\n const { id, attributes, relationships = {} } = postJSON;\n\n if (!id) {\n this.log('error', 'Parse error: \\'id\\' field missing in API data of post');\n continue;\n }\n\n this.log('debug', `Parse post #${id}`);\n\n if (!attributes || typeof attributes !== 'object') {\n this.log('error', `Parse error: 'attributes' field missing in API data of post #${id} or has incorrect type`);\n continue;\n }\n\n // Campaign info\n if (campaign === undefined) {\n const campaignId = ObjectHelper.getProperty(postJSON, 'relationships.campaign.data.id') || null;\n if (!campaignId || typeof campaignId !== 'string') {\n this.log('warn', `Campaign ID missing in API data of post #${id} or has incorrect type` +\n ' - no campaign info will be available until campaign ID is obtained');\n }\n else if (hasIncludedJSON) {\n campaign = this.findInAPIResponseIncludedArray(includedJSON, campaignId, 'campaign');\n }\n }\n\n // Viewability\n const isViewable = pickDefined(attributes.current_user_can_view, true);\n if (attributes.current_user_can_view === undefined) {\n this.log('warn', `'current_user_can_view' attribute missing in API data of post #${id} - assuming post is viewable`);\n }\n\n // Get downloadables from relationships\n let audio: Downloadable<AudioMediaItem> | null = null;\n let audioPreview: Downloadable<AudioMediaItem> | null = null;\n let images: Downloadable<DefaultImageMediaItem>[] = [];\n let attachments: Downloadable<AttachmentMediaItem>[] = [];\n if (hasIncludedJSON) {\n const downloadables = this.fetchDownloadablesFromRelationships(\n relationships,\n {\n 'audio': 'audio items',\n 'audio_preview': 'audio preview items',\n 'images': 'images',\n 'attachments_media': 'attachments'\n },\n includedJSON,\n `post #${id}`,\n false\n );\n audio = downloadables.audio?.[0] as Downloadable<AudioMediaItem> || null;\n audioPreview = downloadables.audio_preview?.[0] as Downloadable<AudioMediaItem> || null;\n images = downloadables.images as Downloadable<DefaultImageMediaItem>[] || [];\n attachments = downloadables.attachments_media as Downloadable<AttachmentMediaItem>[] || [];\n }\n\n // Post content and teaser\n const content = this.#parseContent(attributes);\n const teaserText = this.#parseContent({\n content: attributes.teaser_text,\n content_json_string: attributes.teaser_text_json_string\n });\n\n // Get inline media from content (currently only images and linked attachments supported)\n const linkedAttachments: LinkedAttachment[] = [];\n const inlineMediaBody = content || teaserText;\n if (hasIncludedJSON && inlineMediaBody) {\n const inlineMedia = this.parseInlineMedia(id, inlineMediaBody, includedJSON);\n images.push(...inlineMedia.images);\n linkedAttachments.push(...inlineMedia.linkedAttachments);\n }\n\n // Video preview\n let videoPreview: Downloadable<VideoMediaItem> | null = null;\n const vidPreviewJSON = attributes.video_preview;\n if (vidPreviewJSON && typeof vidPreviewJSON === 'object') {\n videoPreview = this.#getVideoMediaItemFromAttr(vidPreviewJSON, includedJSON, id);\n if (!videoPreview.downloadURL && !videoPreview.displayURL) {\n this.log('warn', `Video preview for post #${id} is missing URLs`);\n }\n }\n\n // Video - `postType` is 'video_external_file' and has `postFile`\n let video: Downloadable<VideoMediaItem> | null = null;\n const postFileJSON = attributes.post_file;\n const hasPostFile = postFileJSON && typeof postFileJSON === 'object';\n if (attributes.post_type === PostType.Video && hasPostFile) {\n video = this.#getVideoMediaItemFromAttr(postFileJSON, includedJSON, id);\n if (!video.downloadURL && !video.displayURL) {\n this.log('warn', `Video for post #${id} is missing URLs`);\n }\n }\n // Repeat for 'podcast' type - but note that `postFile` here can be audio or video.\n // We are only interested in video. For audio, info should have already been obtained\n // through relationships above.\n if (attributes.post_type === PostType.Podcast && hasPostFile) {\n video = this.#getVideoMediaItemFromAttr(postFileJSON, includedJSON, id, true);\n }\n\n if (attributes.post_type === PostType.Podcast && isViewable && !video && !audio) {\n this.log('warn', `Post #${id} is podcast type and is viewable, but no video or audio was found`);\n }\n\n // Cover image\n let coverImage: PostCoverImageMediaItem | null = null;\n if (attributes.image) {\n coverImage = {\n type: 'image',\n id: `post:${id}:cover`,\n filename: isViewable ? 'cover-image' : 'cover-image-preview',\n mimeType: null,\n imageType: 'postCoverImage',\n imageURLs: {\n large: ObjectHelper.getProperty(attributes, 'image.large_url') || null,\n thumbSquareLarge: ObjectHelper.getProperty(attributes, 'image.thumb_square_large_url') || null,\n thumbSquare: ObjectHelper.getProperty(attributes, 'image.thumb_square_url') || null,\n thumb: ObjectHelper.getProperty(attributes, 'image.thumb_url') || null,\n default: ObjectHelper.getProperty(attributes, 'image.url') || null\n }\n };\n }\n\n // Thumbnail\n let thumbnail: PostThumbnailMediaItem | null = null;\n if (attributes.thumbnail) {\n thumbnail = {\n type: 'image',\n id: `post:${id}:thumbnail`,\n filename: isViewable ? 'thumbnail' : 'thumbnail-preview',\n mimeType: null,\n imageType: 'postThumbnail',\n imageURLs: {\n large: ObjectHelper.getProperty(attributes, 'thumbnail.large') || null,\n large2: ObjectHelper.getProperty(attributes, 'thumbnail.large_2') || null,\n square: ObjectHelper.getProperty(attributes, 'thumbnail.square') || null,\n default: ObjectHelper.getProperty(attributes, 'thumbnail.url') || null\n }\n };\n }\n\n // Audio items doesn't have thumbnail - use first image or thumbnail\n if (audio) {\n audio.thumbnailURL =\n images[0]?.imageURLs.default ||\n thumbnail?.imageURLs.default || null;\n }\n if (audioPreview) {\n audioPreview.thumbnailURL =\n images[0]?.imageURLs.default ||\n thumbnail?.imageURLs.default || null;\n }\n\n // Embed\n const embedJSON = attributes.embed;\n let embed: PostEmbed | null = null;\n if (embedJSON && typeof embedJSON === 'object') {\n let embedType: PostEmbed['type'];\n switch (attributes.post_type) {\n case 'video_embed':\n embedType = 'videoEmbed';\n break;\n case 'link':\n embedType = 'linkEmbed';\n break;\n default:\n embedType = 'unknownEmbed';\n }\n embed = {\n id: `${id}-embed`,\n postId: id,\n postURL: attributes.url || null,\n type: embedType,\n description: embedJSON.descripton || null,\n html: embedJSON.html || null,\n provider: embedJSON.provider || null,\n providerURL: embedJSON.provider_url || null,\n subject: embedJSON.subject || null,\n url: embedJSON.url || null,\n thumbnailURL: ObjectHelper.getProperty(attributes, 'image.large_url') || null // Use cover image\n };\n }\n\n // Tiers\n let tiers: Tier[] = [];\n const tierData = ObjectHelper.getProperty(postJSON, 'relationships.access_rules.data');\n if (Array.isArray(tierData) && campaign) {\n const c = campaign;\n tiers = tierData.reduce<Tier[]>((result, t) => {\n const id = ObjectHelper.getProperty(t, 'id');\n if (id) {\n const tier = this.findInAPIResponseIncludedArray(includedJSON, id, 'tier', c);\n if (tier) {\n result.push(tier);\n }\n }\n return result;\n }, []);\n }\n if (tiers.length === 0) {\n this.log('warn', `Could not obtain tier info for post #${id}`);\n }\n\n // Collections\n let collections: Collection[] = [];\n const collectionsData = ObjectHelper.getProperty(postJSON, 'relationships.collections.data');\n if (Array.isArray(collectionsData)) {\n collections = collectionsData.reduce<Collection[]>((result, c) => {\n const id = ObjectHelper.getProperty(c, 'id');\n if (id) {\n const collection = this.findInAPIResponseIncludedArray(includedJSON, id, 'collection');\n if (collection) {\n result.push(collection);\n }\n }\n return result;\n }, []);\n }\n\n // Tags\n let tags: PostTag[] = [];\n const tagsData = ObjectHelper.getProperty(postJSON, 'relationships.user_defined_tags.data');\n if (Array.isArray(tagsData)) {\n tags = tagsData.reduce<PostTag[]>((result, t) => {\n const id = ObjectHelper.getProperty(t, 'id');\n if (id) {\n const tag = this.findInAPIResponseIncludedArray(includedJSON, id, 'post_tag');\n if (tag) {\n result.push(tag);\n }\n }\n return result;\n }, []);\n }\n\n const post: Post = {\n id,\n type: 'post',\n postType: attributes.post_type || null,\n isViewable,\n url: attributes.url || null,\n title: attributes.title || null,\n content,\n contentText: stripHtml(content || '').result,\n teaserText,\n publishedAt: attributes.published_at || null,\n editedAt: attributes.edited_at || null,\n commentCount: attributes.comment_count || 0,\n coverImage,\n thumbnail,\n tiers,\n collections,\n tags,\n embed,\n attachments,\n linkedAttachments,\n audio,\n audioPreview,\n images,\n videoPreview,\n video,\n campaign: null,\n raw: json\n };\n\n this.log('debug', `Done parsing post #${id}`);\n\n collection.items.push(post);\n }\n\n if (campaign) {\n this.log('debug', `Campaign #${campaign.id} found while parsing posts`);\n for (const post of collection.items) {\n post.campaign = campaign;\n }\n }\n else {\n this.log('warn', 'No campaign info found while parsing posts');\n }\n\n this.log('debug', 'Done parsing posts');\n\n return collection;\n }\n\n /**\n * Returns `content` if value is not empty.\n * Otherwise, returns HTML assembled from `content_json_string`.\n * \n * Contributed by: Fabelwesen (https://github.com/Fabelwesen)\n * Origin: https://github.com/patrickkfkan/patreon-dl/issues/119\n * \n * @param data \n * @returns HTML or null\n */\n #parseContent(data: { content?: string | null; content_json_string?: string | null }) {\n const { content, content_json_string } = data;\n if (typeof content === 'string' && content.trim()) {\n return content;\n }\n if (!content_json_string) {\n return null;\n }\n try {\n const doc = JSON.parse(content_json_string);\n let parsedHTML = '';\n\n function extractHTML(node: any) {\n if (!node) return;\n\n if (node.type === 'text') {\n if (node.marks && node.marks.find((m: any) => m.type === 'link')) {\n const linkMark = node.marks.find((m: any) => m.type === 'link');\n parsedHTML += `<a href=\"${linkMark.attrs.href}\" target=\"_blank\">${node.text || ''}</a>`;\n } else {\n // You can expand this to handle bold, italic, etc.\n parsedHTML += (String(node.text) || '');\n }\n } else if (node.type === 'paragraph' || node.type === 'heading' || node.type === 'listItem') {\n parsedHTML += '<p>';\n if (node.content && Array.isArray(node.content)) {\n node.content.forEach(extractHTML);\n }\n parsedHTML += '</p>';\n } else if (node.type === 'image') {\n // Ensure media_id is passed so the local web server can link downloaded assets\n const mediaIdAttr = (node.attrs && node.attrs.media_id) ? `data-media-id=\"${node.attrs.media_id}\" ` : '';\n const srcAttr = (node.attrs && node.attrs.src) ? node.attrs.src : '';\n parsedHTML += `<img ${mediaIdAttr}src=\"${srcAttr}\" />`;\n } else if (node.content && Array.isArray(node.content)) {\n node.content.forEach(extractHTML);\n }\n }\n\n extractHTML(doc);\n return parsedHTML;\n } catch (e) {\n this.log('warn', 'Failed to parse content_json_string:', e);\n }\n return null;\n }\n\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict: true): VideoMediaItem | null;\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict?: false): VideoMediaItem;\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict = false) {\n let miFromIncluded: MediaItem | null = null;\n const _mediaId = attrJSON.media_id !== undefined ? attrJSON.media_id.toString() : null;\n const hasIncludedJSON = includedJSON && Array.isArray(includedJSON)\n if (_mediaId && hasIncludedJSON) {\n // Fetch item from 'included' array, matching `media_id`\n miFromIncluded = this.findInAPIResponseIncludedArray(includedJSON, _mediaId, 'media', 'video');\n }\n const vidInc = miFromIncluded && miFromIncluded.type === 'video' ? miFromIncluded : null;\n if (strict && !miFromIncluded) {\n return null;\n }\n const mediaId = _mediaId || postId; // Fallback to post ID\n\n const { download_url: downloadURL = null, url: displayURL = null } = attrJSON;\n\n // Convert `attrJSON` to Downloadable (VideoMediaItem)\n return {\n type: 'video',\n id: mediaId,\n filename: vidInc?.filename || null,\n mimeType: vidInc?.mimeType || null,\n createdAt: vidInc?.createdAt || null,\n size: {\n width: attrJSON.width || vidInc?.size.width,\n height: attrJSON.height || vidInc?.size.height\n },\n duration: attrJSON.duration || vidInc?.duration,\n downloadURL: downloadURL || vidInc?.downloadURL,\n displayURL: displayURL || vidInc?.downloadURL,\n thumbnailURL: vidInc?.thumbnailURL || null\n };\n };\n}\n"]}
1
+ {"version":3,"file":"PostParser.js","sourceRoot":"","sources":["../../src/parsers/PostParser.ts"],"names":[],"mappings":"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","sourcesContent":["import { stripHtml } from 'string-strip-html';\nimport { type Campaign } from '../entities/Campaign.js';\nimport { isYouTubeEmbed, type Downloadable } from '../entities/Downloadable.js';\nimport { type AttachmentMediaItem, type AudioMediaItem, type DefaultImageMediaItem, type MediaItem, type PostCoverImageMediaItem, type PostThumbnailMediaItem, type VideoMediaItem } from '../entities/MediaItem.js';\nimport { type LinkedAttachment, PostType, type Post, type PostList as PostList, type PostEmbed, type Collection, type PostTag } from '../entities/Post.js';\nimport { type Tier } from '../entities/Reward.js';\nimport { pickDefined } from '../utils/Misc.js';\nimport ObjectHelper from '../utils/ObjectHelper.js';\nimport Parser from './Parser.js';\nimport URLHelper from '../utils/URLHelper.js';\nimport type Logger from '../utils/logging/Logger.js';\nimport type Fetcher from '../utils/Fetcher.js';\n\nexport default class PostParser extends Parser {\n\n protected name = 'PostParser';\n\n #fetcher: Fetcher;\n\n constructor(fetcher: Fetcher, logger?: Logger | null) {\n super(logger);\n this.#fetcher = fetcher;\n }\n\n async parsePostsAPIResponse(json: any, src: string): Promise<PostList> {\n\n this.log('debug', `Parse API data obtained from \"${src}\"`);\n\n /*If (json.errors) {\n this.log('error', `API response error:`, json.errors);\n return null;\n }*/\n\n const includedJSON = json.included;\n const dataJSON = json.data;\n let postsJSONArray: any[];\n // Check if API data consists of just a single post (not an array).\n // If so, place the post data in an array.\n if (dataJSON && !Array.isArray(dataJSON) && dataJSON.type === 'post') {\n postsJSONArray = [ dataJSON ];\n }\n // If API data is an array, filter out those matching 'post' type.\n else if (dataJSON && Array.isArray(dataJSON)) {\n postsJSONArray = dataJSON.filter((data) => data.type === 'post');\n }\n else {\n // No posts found\n postsJSONArray = [];\n }\n const collection: PostList = {\n items: [],\n total: ObjectHelper.getProperty(json, 'meta.pagination.total') || null,\n // src could be a file, in which case there'll be no nextURL.\n nextURL: URLHelper.validateURL(src) ? this.parseCollectionNextURL(json, src) : null\n };\n\n let hasIncludedJSON = true;\n if (!includedJSON || !Array.isArray(includedJSON)) {\n this.log('warn', `'included' field missing in API data obtained from \"${src}\" or has incorrect type - no media items and campaign info will be returned`);\n hasIncludedJSON = false;\n }\n\n if (postsJSONArray.length === 0) {\n this.log('warn', `No posts found in API data obtained from \"${src}\"`);\n return collection;\n }\n if (postsJSONArray.length > 1) {\n this.log('debug', `${postsJSONArray.length} posts found - iterate and parse`);\n }\n else {\n this.log('debug', '1 post found - parse');\n }\n\n let campaign: Campaign | null | undefined;\n\n for (const postJSON of postsJSONArray) {\n if (!postJSON || typeof postJSON !== 'object') {\n this.log('error', 'Parse error: API data of post has incorrect type');\n continue;\n }\n\n const { id, attributes, relationships = {} } = postJSON;\n\n if (!id) {\n this.log('error', 'Parse error: \\'id\\' field missing in API data of post');\n continue;\n }\n\n this.log('debug', `Parse post #${id}`);\n\n if (!attributes || typeof attributes !== 'object') {\n this.log('error', `Parse error: 'attributes' field missing in API data of post #${id} or has incorrect type`);\n continue;\n }\n\n // Campaign info\n if (campaign === undefined) {\n const campaignId = ObjectHelper.getProperty(postJSON, 'relationships.campaign.data.id') || null;\n if (!campaignId || typeof campaignId !== 'string') {\n this.log('warn', `Campaign ID missing in API data of post #${id} or has incorrect type` +\n ' - no campaign info will be available until campaign ID is obtained');\n }\n else if (hasIncludedJSON) {\n campaign = this.findInAPIResponseIncludedArray(includedJSON, campaignId, 'campaign');\n }\n }\n\n // Viewability\n const isViewable = pickDefined(attributes.current_user_can_view, true);\n if (attributes.current_user_can_view === undefined) {\n this.log('warn', `'current_user_can_view' attribute missing in API data of post #${id} - assuming post is viewable`);\n }\n\n // Get downloadables from relationships\n let audio: Downloadable<AudioMediaItem> | null = null;\n let audioPreview: Downloadable<AudioMediaItem> | null = null;\n let images: Downloadable<DefaultImageMediaItem>[] = [];\n let attachments: Downloadable<AttachmentMediaItem>[] = [];\n if (hasIncludedJSON) {\n const downloadables = this.fetchDownloadablesFromRelationships(\n relationships,\n {\n 'audio': 'audio items',\n 'audio_preview': 'audio preview items',\n 'images': 'images',\n 'attachments_media': 'attachments'\n },\n includedJSON,\n `post #${id}`,\n false\n );\n audio = downloadables.audio?.[0] as Downloadable<AudioMediaItem> || null;\n audioPreview = downloadables.audio_preview?.[0] as Downloadable<AudioMediaItem> || null;\n images = downloadables.images as Downloadable<DefaultImageMediaItem>[] || [];\n attachments = downloadables.attachments_media as Downloadable<AttachmentMediaItem>[] || [];\n }\n\n // If post metadata has image_order, use that to arrange images\n const imageOrder = ObjectHelper.getProperty(attributes, 'post_metadata.image_order');\n if (Array.isArray(imageOrder) && images.length > 0) {\n const orderedImages: DefaultImageMediaItem[] = [];\n for (const imageId of imageOrder) {\n const imgIndex = images.findIndex((i) => i.id === imageId);\n if (imgIndex !== -1) {\n orderedImages.push(images[imgIndex]);\n images.splice(imgIndex, 1);\n }\n else {\n this.log('warn', `Image with ID \"${imageId}\" listed in 'image_order' metadata of post #${id} not found among images obtained from relationships - skipping`);\n }\n }\n if (images.length > 0) {\n this.log('warn', `${images.length} images obtained from relationships of post #${id} not listed in 'image_order' metadata - appending at the end`);\n orderedImages.push(...images);\n }\n images = orderedImages.map((img, index) => ({ ...img, index }));\n }\n else if (images.length > 0) {\n images.forEach((img, index) => {\n img.index = index;\n });\n }\n\n // Post content and teaser\n const content = this.#parseContent(attributes);\n const teaserText = this.#parseContent({\n content: attributes.teaser_text,\n content_json_string: attributes.teaser_text_json_string\n });\n\n // Get inline media from content (currently only images and linked attachments supported)\n const linkedAttachments: LinkedAttachment[] = [];\n const inlineMediaBody = content || teaserText;\n if (hasIncludedJSON && inlineMediaBody) {\n const inlineMedia = this.parseInlineMedia(id, inlineMediaBody, includedJSON);\n inlineMedia.images.forEach((img, index) => {\n img.index = images.length + index;\n });\n images.push(...inlineMedia.images);\n linkedAttachments.push(...inlineMedia.linkedAttachments);\n }\n\n // Video preview\n let videoPreview: Downloadable<VideoMediaItem> | null = null;\n const vidPreviewJSON = attributes.video_preview;\n if (vidPreviewJSON && typeof vidPreviewJSON === 'object') {\n videoPreview = this.#getVideoMediaItemFromAttr(vidPreviewJSON, includedJSON, id);\n if (!videoPreview.downloadURL && !videoPreview.displayURL) {\n this.log('warn', `Video preview for post #${id} is missing URLs`);\n }\n }\n\n // Video - `postType` is 'video_external_file' and has `postFile`\n let video: Downloadable<VideoMediaItem> | null = null;\n const postFileJSON = attributes.post_file;\n const hasPostFile = postFileJSON && typeof postFileJSON === 'object';\n if (attributes.post_type === PostType.Video && hasPostFile) {\n video = this.#getVideoMediaItemFromAttr(postFileJSON, includedJSON, id);\n if (!video.downloadURL && !video.displayURL) {\n this.log('warn', `Video for post #${id} is missing URLs`);\n }\n }\n // Repeat for 'podcast' type - but note that `postFile` here can be audio or video.\n // We are only interested in video. For audio, info should have already been obtained\n // through relationships above.\n if (attributes.post_type === PostType.Podcast && hasPostFile) {\n video = this.#getVideoMediaItemFromAttr(postFileJSON, includedJSON, id, true);\n }\n\n if (attributes.post_type === PostType.Podcast && isViewable && !video && !audio) {\n this.log('warn', `Post #${id} is podcast type and is viewable, but no video or audio was found`);\n }\n\n // Cover image\n let coverImage: PostCoverImageMediaItem | null = null;\n if (attributes.image) {\n coverImage = {\n type: 'image',\n id: `post:${id}:cover`,\n filename: isViewable ? 'cover-image' : 'cover-image-preview',\n mimeType: null,\n imageType: 'postCoverImage',\n imageURLs: {\n large: ObjectHelper.getProperty(attributes, 'image.large_url') || null,\n thumbSquareLarge: ObjectHelper.getProperty(attributes, 'image.thumb_square_large_url') || null,\n thumbSquare: ObjectHelper.getProperty(attributes, 'image.thumb_square_url') || null,\n thumb: ObjectHelper.getProperty(attributes, 'image.thumb_url') || null,\n default: ObjectHelper.getProperty(attributes, 'image.url') || null\n }\n };\n }\n\n // Thumbnail\n let thumbnail: PostThumbnailMediaItem | null = null;\n if (attributes.thumbnail) {\n thumbnail = {\n type: 'image',\n id: `post:${id}:thumbnail`,\n filename: isViewable ? 'thumbnail' : 'thumbnail-preview',\n mimeType: null,\n imageType: 'postThumbnail',\n imageURLs: {\n large: ObjectHelper.getProperty(attributes, 'thumbnail.large') || null,\n large2: ObjectHelper.getProperty(attributes, 'thumbnail.large_2') || null,\n square: ObjectHelper.getProperty(attributes, 'thumbnail.square') || null,\n default: ObjectHelper.getProperty(attributes, 'thumbnail.url') || null\n }\n };\n }\n\n // Audio items doesn't have thumbnail - use first image or thumbnail\n if (audio) {\n audio.thumbnailURL =\n images[0]?.imageURLs.default ||\n thumbnail?.imageURLs.default || null;\n }\n if (audioPreview) {\n audioPreview.thumbnailURL =\n images[0]?.imageURLs.default ||\n thumbnail?.imageURLs.default || null;\n }\n\n // Embed\n const embedJSON = attributes.embed;\n let embed: PostEmbed | null = null;\n if (embedJSON && typeof embedJSON === 'object') {\n let embedType: PostEmbed['type'];\n switch (attributes.post_type) {\n case 'video_embed':\n embedType = 'videoEmbed';\n break;\n case 'link':\n embedType = 'linkEmbed';\n break;\n default:\n embedType = 'unknownEmbed';\n }\n embed = {\n id: `${id}-embed`,\n postId: id,\n postURL: attributes.url || null,\n type: embedType,\n description: embedJSON.descripton || null,\n html: embedJSON.html || null,\n provider: embedJSON.provider || null,\n providerURL: embedJSON.provider_url || null,\n subject: embedJSON.subject || null,\n url: embedJSON.url || null,\n thumbnailURL: ObjectHelper.getProperty(attributes, 'image.large_url') || null // Use cover image\n };\n }\n // YouTube embeds can have 0-byte thumbnail:\n // https://github.com/patrickkfkan/patreon-dl/issues/120\n if (embed && isYouTubeEmbed(embed) && embed.url) {\n // Credit: @Fabelwesen (https://github.com/Fabelwesen)\n const ytMatch = embed.url.match(/(?:youtu\\.be\\/|youtube\\.com\\/(?:embed\\/|v\\/|watch\\?v=|watch\\?.+&v=))([a-zA-Z0-9_-]{11})/);\n if (ytMatch) {\n const ytId = ytMatch[1];\n // Use maxresdefault for best quality\n const ytThumbnailURL = `https://img.youtube.com/vi/${ytId}/maxresdefault.jpg`;\n if (await this.#fetcher.test(ytThumbnailURL)) {\n this.log('debug', `Set YouTube embed thumbnail URL to \"${ytThumbnailURL}\"`);\n const originalThumbnailURL = embed.thumbnailURL;\n embed.thumbnailURL = ytThumbnailURL;\n const __replace = (o: { imageURLs: Record<string, string | null | undefined> }) => {\n Object.keys(o.imageURLs).forEach((key) => {\n if (o?.imageURLs[key] === originalThumbnailURL) {\n o.imageURLs[key] = ytThumbnailURL;\n }\n });\n }\n if (coverImage) {\n __replace(coverImage);\n }\n if (thumbnail) {\n __replace(thumbnail);\n }\n if (images) {\n for (const img of images) {\n __replace(img);\n if (img.thumbnailURL === originalThumbnailURL) {\n img.thumbnailURL = ytThumbnailURL;\n }\n }\n }\n }\n else {\n this.log('debug', `Test failed for YouTube thumbnail URL \"${ytThumbnailURL}\". Going to use URL from API data.`);\n }\n }\n }\n\n // Tiers\n let tiers: Tier[] = [];\n const tierData = ObjectHelper.getProperty(postJSON, 'relationships.access_rules.data');\n if (Array.isArray(tierData) && campaign) {\n const c = campaign;\n tiers = tierData.reduce<Tier[]>((result, t) => {\n const id = ObjectHelper.getProperty(t, 'id');\n if (id) {\n const tier = this.findInAPIResponseIncludedArray(includedJSON, id, 'tier', c);\n if (tier) {\n result.push(tier);\n }\n }\n return result;\n }, []);\n }\n if (tiers.length === 0) {\n this.log('warn', `Could not obtain tier info for post #${id}`);\n }\n\n // Collections\n let collections: Collection[] = [];\n const collectionsData = ObjectHelper.getProperty(postJSON, 'relationships.collections.data');\n if (Array.isArray(collectionsData)) {\n collections = collectionsData.reduce<Collection[]>((result, c) => {\n const id = ObjectHelper.getProperty(c, 'id');\n if (id) {\n const collection = this.findInAPIResponseIncludedArray(includedJSON, id, 'collection');\n if (collection) {\n result.push(collection);\n }\n }\n return result;\n }, []);\n }\n\n // Tags\n let tags: PostTag[] = [];\n const tagsData = ObjectHelper.getProperty(postJSON, 'relationships.user_defined_tags.data');\n if (Array.isArray(tagsData)) {\n tags = tagsData.reduce<PostTag[]>((result, t) => {\n const id = ObjectHelper.getProperty(t, 'id');\n if (id) {\n const tag = this.findInAPIResponseIncludedArray(includedJSON, id, 'post_tag');\n if (tag) {\n result.push(tag);\n }\n }\n return result;\n }, []);\n }\n\n const post: Post = {\n id,\n type: 'post',\n postType: attributes.post_type || null,\n isViewable,\n url: attributes.url || null,\n title: attributes.title || null,\n content,\n contentText: stripHtml(content || '').result,\n teaserText,\n publishedAt: attributes.published_at || null,\n editedAt: attributes.edited_at || null,\n commentCount: attributes.comment_count || 0,\n coverImage,\n thumbnail,\n tiers,\n collections,\n tags,\n embed,\n attachments,\n linkedAttachments,\n audio,\n audioPreview,\n images,\n videoPreview,\n video,\n campaign: null,\n raw: json\n };\n\n this.log('debug', `Done parsing post #${id}`);\n\n collection.items.push(post);\n }\n\n if (campaign) {\n this.log('debug', `Campaign #${campaign.id} found while parsing posts`);\n for (const post of collection.items) {\n post.campaign = campaign;\n }\n }\n else {\n this.log('warn', 'No campaign info found while parsing posts');\n }\n\n this.log('debug', 'Done parsing posts');\n\n return collection;\n }\n\n /**\n * Returns `content` if value is not empty.\n * Otherwise, returns HTML assembled from `content_json_string`.\n * \n * Contributed by: Fabelwesen (https://github.com/Fabelwesen)\n * Origin: https://github.com/patrickkfkan/patreon-dl/issues/119\n * \n * @param data \n * @returns HTML or null\n */\n #parseContent(data: { content?: string | null; content_json_string?: string | null }) {\n const { content, content_json_string } = data;\n if (typeof content === 'string' && content.trim()) {\n return content;\n }\n if (!content_json_string) {\n return null;\n }\n try {\n const doc = JSON.parse(content_json_string);\n let parsedHTML = '';\n\n function extractHTML(node: any) {\n if (!node) return;\n\n if (node.type === 'text') {\n if (node.marks && node.marks.find((m: any) => m.type === 'link')) {\n const linkMark = node.marks.find((m: any) => m.type === 'link');\n parsedHTML += `<a href=\"${linkMark.attrs.href}\" target=\"_blank\">${node.text || ''}</a>`;\n } else {\n // You can expand this to handle bold, italic, etc.\n parsedHTML += (String(node.text) || '');\n }\n } else if (node.type === 'paragraph' || node.type === 'heading' || node.type === 'listItem') {\n parsedHTML += '<p>';\n if (node.content && Array.isArray(node.content)) {\n node.content.forEach(extractHTML);\n }\n parsedHTML += '</p>';\n } else if (node.type === 'image') {\n // Ensure media_id is passed so the local web server can link downloaded assets\n const mediaIdAttr = (node.attrs && node.attrs.media_id) ? `data-media-id=\"${node.attrs.media_id}\" ` : '';\n const srcAttr = (node.attrs && node.attrs.src) ? node.attrs.src : '';\n parsedHTML += `<img ${mediaIdAttr}src=\"${srcAttr}\" />`;\n } else if (node.content && Array.isArray(node.content)) {\n node.content.forEach(extractHTML);\n }\n }\n\n extractHTML(doc);\n return parsedHTML;\n } catch (e) {\n this.log('warn', 'Failed to parse content_json_string:', e);\n }\n return null;\n }\n\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict: true): VideoMediaItem | null;\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict?: false): VideoMediaItem;\n #getVideoMediaItemFromAttr(attrJSON: any, includedJSON: any, postId: string, strict = false) {\n let miFromIncluded: MediaItem | null = null;\n const _mediaId = attrJSON.media_id !== undefined ? attrJSON.media_id.toString() : null;\n const hasIncludedJSON = includedJSON && Array.isArray(includedJSON)\n if (_mediaId && hasIncludedJSON) {\n // Fetch item from 'included' array, matching `media_id`\n miFromIncluded = this.findInAPIResponseIncludedArray(includedJSON, _mediaId, 'media', 'video');\n }\n const vidInc = miFromIncluded && miFromIncluded.type === 'video' ? miFromIncluded : null;\n if (strict && !miFromIncluded) {\n return null;\n }\n const mediaId = _mediaId || postId; // Fallback to post ID\n\n const { download_url: downloadURL = null, url: displayURL = null } = attrJSON;\n\n // Convert `attrJSON` to Downloadable (VideoMediaItem)\n return {\n type: 'video',\n id: mediaId,\n filename: vidInc?.filename || null,\n mimeType: vidInc?.mimeType || null,\n createdAt: vidInc?.createdAt || null,\n size: {\n width: attrJSON.width || vidInc?.size.width,\n height: attrJSON.height || vidInc?.size.height\n },\n duration: attrJSON.duration || vidInc?.duration,\n downloadURL: downloadURL || vidInc?.downloadURL,\n displayURL: displayURL || vidInc?.downloadURL,\n thumbnailURL: vidInc?.thumbnailURL || null\n };\n };\n}\n"]}
@@ -59,6 +59,7 @@ export default class Fetcher {
59
59
  }>;
60
60
  abort: () => void;
61
61
  }>;
62
+ test(url: string): Promise<boolean>;
62
63
  getPageWithPuppeteer(url: string): Promise<string>;
63
64
  protected log(level: LogLevel, ...msg: Array<any>): void;
64
65
  get proxyAgent(): Dispatcher | undefined;
@@ -191,6 +191,11 @@ class Fetcher {
191
191
  }
192
192
  return undefined;
193
193
  }
194
+ async test(url) {
195
+ const request = new Request(url, { method: 'HEAD' });
196
+ const res = await fetch(request, { dispatcher: __classPrivateFieldGet(this, _Fetcher_proxyAgent, "f") });
197
+ return res.ok;
198
+ }
194
199
  async getPageWithPuppeteer(url) {
195
200
  this.log('debug', `Fetch "${url}" with Puppeteer`);
196
201
  const browser = await puppeteer.launch({ headless: true });