patreon-dl 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,7 +23,9 @@ You can run `patreon-dl` from the command-line or [use it as a library](./docs/L
23
23
 
24
24
  ### Limitations
25
25
 
26
- - Embedded links are not followed; only info about the embed is saved.
26
+ - Embedded links are not followed; only info about the embed is saved. Exception:
27
+ - YouTube video link - in which case the video is downloaded; or
28
+ - An external downloader is configured for the link provider.
27
29
 
28
30
  ### FFmpeg dependency
29
31
 
@@ -33,9 +35,9 @@ You can run `patreon-dl` from the command-line or [use it as a library](./docs/L
33
35
 
34
36
  Not all video downloads require FFmpeg, but you should have it installed on your system anyway.
35
37
 
36
- ### Embedded YouTube videos - Premium access
38
+ ### Embedded YouTube videos / links - Premium access
37
39
 
38
- `patreon-dl` supports downloading embedded YouTube videos. In addition, if you have a YouTube Premium subscription, you can connect `patreon-dl` to your account and download videos at qualities available only to Premium accounts (e.g. '1080p Premium'). For CLI users, you would configure `patreon-dl` as follows:
40
+ `patreon-dl` supports downloading embedded YouTube videos or from embedded YouTube video links. In addition, if you have a YouTube Premium subscription, you can connect `patreon-dl` to your account and download videos at qualities available only to Premium accounts (e.g. '1080p Premium'). For CLI users, you would configure `patreon-dl` as follows:
39
41
 
40
42
  ```
41
43
  $ patreon-dl --configure-youtube
@@ -43,9 +45,9 @@ $ patreon-dl --configure-youtube
43
45
 
44
46
  > ...or you may just refer to the next section on how to download enhanecd-quality videos without a Premium account.
45
47
 
46
- ### Embedded videos - external downloader
48
+ ### Embedded videos / links - external downloader
47
49
 
48
- You can specify external programs to download embedded videos. For YouTube videos, this will replace the built-in downloader. See the [example config](./example-embed.conf) on how to do this.
50
+ You can specify external programs to download embedded videos or from embedded links. For YouTube videos, this will replace the built-in downloader. See the [example config](./example-embed.conf) on how to do this.
49
51
 
50
52
  > The example config utilizes [yt-dlp](https://github.com/yt-dlp/yt-dlp), a popular program capable of downloading YouTube and Vimeo content. As of current release, `yt-dlp` is also able to download Premium-quality YouTube videos without a Premium account.
51
53
 
@@ -219,6 +221,11 @@ Note that you can override an option from a configuration file with one provided
219
221
 
220
222
  ## Changelog
221
223
 
224
+ v2.2.0
225
+ - Widen scope of external downloaders to any type of embed (previously only works for video embeds) ([#51](https://github.com/patrickkfkan/patreon-dl/issues/51))
226
+ - YouTube downloading now covers embedded YT links
227
+ - Fix attachment filenames sometimes have wrong extension
228
+
222
229
  v2.1.1
223
230
  - Fix multiple abort signal listeners triggering warning ([#48](https://github.com/patrickkfkan/patreon-dl/issues/48))
224
231
  - Fix YouTube embeds failing to download due to YT changes ([#50](https://github.com/patrickkfkan/patreon-dl/issues/50))
@@ -1 +1 @@
1
- {"version":3,"file":"PostDownloader.d.ts","sourceRoot":"","sources":["../../src/downloaders/PostDownloader.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAyB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGhG,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAShD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,UAAU,CAAC,IAAI,CAAC;;IAE1D,MAAM,CAAC,OAAO,SAAW;IAEzB,IAAI,SAAoB;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoe9C,aAAa,CAAC,MAAM,CAAC,EAAE,WAAW;CA0HzC"}
1
+ {"version":3,"file":"PostDownloader.d.ts","sourceRoot":"","sources":["../../src/downloaders/PostDownloader.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAyB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGhG,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAShD,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,UAAU,CAAC,IAAI,CAAC;;IAE1D,MAAM,CAAC,OAAO,SAAW;IAEzB,IAAI,SAAoB;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoe9C,aAAa,CAAC,MAAM,CAAC,EAAE,WAAW;CAyIzC"}
@@ -178,7 +178,7 @@ class PostDownloader extends Downloader {
178
178
  const hasAttachments = post.attachments.length > 0;
179
179
  const hasAudio = !!post.audio || !!post.audioPreview;
180
180
  const hasImages = post.images.length > 0;
181
- const hasVideo = !!post.video || !!post.videoPreview || !!(post.embed && post.embed.type === 'videoEmbed');
181
+ const hasVideo = !!post.video || !!post.videoPreview || !!(post.embed && (post.embed.type === 'videoEmbed' || isYouTubeEmbed(post.embed)));
182
182
  let skip = false;
183
183
  if (postsWithMediaType === 'none') {
184
184
  skip = hasAttachments || hasAudio || hasImages || hasVideo;
@@ -506,6 +506,24 @@ _PostDownloader_startPromise = new WeakMap(), _PostDownloader_instances = new We
506
506
  }
507
507
  return incContent.includes(mediaType);
508
508
  };
509
+ const __getEmbedTask = () => {
510
+ if (!post.embed) {
511
+ return null;
512
+ }
513
+ const hasEmbedDownloader = DownloadTaskFactory.findEmbedDownloader(this.config.embedDownloaders, post.embed.provider);
514
+ const isDownloadableVideo = isYouTubeEmbed(post.embed) || (post.embed.type === 'videoEmbed' && hasEmbedDownloader);
515
+ const isDownloadableOther = !isYouTubeEmbed(post.embed) && post.embed.type !== 'videoEmbed' && hasEmbedDownloader;
516
+ if ((isDownloadableVideo && __incContent('video')) || isDownloadableOther) {
517
+ const embedType = post.embed.type === 'videoEmbed' ? ' video' : post.embed.type === 'linkEmbed' ? ' link' : '';
518
+ return {
519
+ target: [post.embed],
520
+ targetName: `post #${post.id} -> embedded ${post.embed.provider}${embedType}`,
521
+ destDir: postDirs.embed,
522
+ fileExistsAction: this.config.fileExistsAction.content
523
+ };
524
+ }
525
+ return null;
526
+ };
509
527
  const batchResult = this.createDownloadTaskBatch(`Post #${post.id} (${post.title})`, signal, __incPreview('audio') && post.audioPreview ? {
510
528
  target: [post.audioPreview],
511
529
  targetName: `post #${post.id} -> audio preview`,
@@ -546,14 +564,7 @@ _PostDownloader_startPromise = new WeakMap(), _PostDownloader_instances = new We
546
564
  targetName: `post #${post.id} -> attachments`,
547
565
  destDir: postDirs.attachments,
548
566
  fileExistsAction: this.config.fileExistsAction.content
549
- } : null, __incContent('video') && post.embed &&
550
- (isYouTubeEmbed(post.embed) || DownloadTaskFactory.findEmbedDownloader(this.config.embedDownloaders, post.embed.provider)) ?
551
- {
552
- target: [post.embed],
553
- targetName: `post #${post.id} -> embedded ${post.embed.provider} video`,
554
- destDir: postDirs.embed,
555
- fileExistsAction: this.config.fileExistsAction.content
556
- } : null);
567
+ } : null, __getEmbedTask());
557
568
  return batchResult;
558
569
  };
559
570
  PostDownloader.version = '1.1.1';
@@ -1 +1 @@
1
- {"version":3,"file":"PostDownloader.js","sourceRoot":"","sources":["../../src/downloaders/PostDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import URLHelper from '../utils/URLHelper.js';\nimport Downloader, { type DownloaderConfig, type DownloaderStartParams } from './Downloader.js';\nimport type DownloadTaskBatch from './task/DownloadTaskBatch.js';\nimport PostParser from '../parsers/PostParser.js';\nimport { type Post } from '../entities/Post.js';\nimport { type Downloadable, isYouTubeEmbed } from '../entities/Downloadable.js';\nimport StatusCache from './cache/StatusCache.js';\nimport { generatePostEmbedSummary, generatePostSummary } from './templates/PostInfo.js';\nimport path from 'path';\nimport { TargetSkipReason } from './DownloaderEvent.js';\nimport DownloadTaskFactory from './task/DownloadTaskFactory.js';\nimport PostsFetcher from './PostsFetcher.js';\n\nexport default class PostDownloader extends Downloader<Post> {\n\n static version = '1.1.1';\n\n name = 'PostDownloader';\n\n #startPromise: Promise<void> | null = null;\n\n start(params?: DownloaderStartParams): Promise<void> {\n\n if (this.#startPromise) {\n throw Error('Downloader already running');\n }\n\n this.#startPromise = new Promise<void>((resolve) => {\n void (async () => {\n const { signal } = params || {};\n const postFetch = this.config.postFetch;\n let batch: DownloadTaskBatch | null = null;\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n const abortHandler = () => {\n void (async () => {\n this.log('info', 'Abort signal received');\n if (batch) {\n await batch.abort();\n }\n })();\n };\n if (signal) {\n signal.addEventListener('abort', abortHandler, { once: true });\n }\n \n if (postFetch.type === 'byUser') {\n this.log('info', `Targeting posts by '${postFetch.vanity}'`);\n }\n else if (postFetch.type === 'byUserId') {\n this.log('info', `Targeting posts by user #${postFetch.userId}`);\n }\n else if (postFetch.type === 'byCollection') {\n this.log('info', `Targeting posts in collection #${postFetch.collectionId}`);\n }\n else { // Single\n this.log('info', `Targeting post #${postFetch.postId}`);\n }\n if (this.#isFetchingMultiplePosts(postFetch) && postFetch.filters) {\n const filterStr = Object.entries(postFetch.filters).map(([ key, value ]) => `${key}=${value}`).join('; ');\n if (filterStr) {\n this.log('info', `Filters: ${filterStr}`);\n }\n }\n \n // Step 1: Get posts (if by user) or target post\n const postsFetcher = new PostsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n postsFetcher.on('statusChange', ({current}) => {\n if (current.status === 'running') {\n this.emit('fetchBegin', { targetType: postsFetcher.getTargetType() });\n }\n });\n postsFetcher.begin();\n \n // Step 2: download posts in each fetched collection\n let downloaded = 0;\n let skippedUnviewable = 0;\n let skippedRedundant = 0;\n let skippedUnmetMediaTypeCriteria = 0;\n let skippedNotInTier = 0;\n let skippedPublishDateOutOfRange = 0;\n let campaignSaved = false;\n const postsParser = new PostParser(this.logger);\n while (postsFetcher.hasNext()) {\n const { collection, aborted, error } = await postsFetcher.next();\n if (!collection || aborted) {\n break;\n }\n if (!collection && error) {\n this.emit('end', { aborted: false, error, message: 'PostsFetcher error' });\n resolve();\n return;\n }\n if (!campaignSaved && collection.posts[0]?.campaign) {\n await this.saveCampaignInfo(collection.posts[0].campaign, signal);\n campaignSaved = true;\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n }\n \n for (const _post of collection.posts) {\n \n let post = _post;\n \n if (this.#isFetchingMultiplePosts(postFetch)) {\n // Refresh to ensure media links have not expired\n this.log('debug', `Refresh post #${_post.id}`);\n const postURL = URLHelper.constructPostsAPIURL({ postId: _post.id });\n const { json } = await this.commonFetchAPI(postURL, signal);\n let refreshed: Post | null = null;\n if (json) {\n refreshed = postsParser.parsePostsAPIResponse(json, postURL).posts[0] || null;\n if (!refreshed) {\n this.log('warn', `Refreshed post #${_post.id} but got empty value - going to use existing data`);\n }\n else if (refreshed.id !== _post.id) {\n this.log('warn', `Refreshed post #${_post.id} but ID mismatch - going to use existing data`);\n refreshed = null;\n }\n }\n else if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n else {\n this.log('warn', `Failed to refresh post #${_post.id} - going to use existing data`);\n }\n if (refreshed) {\n this.log('debug', `Use refreshed post data #${refreshed.id}`);\n post = refreshed;\n }\n }\n \n this.emit('targetBegin', { target: post });\n \n // Step 4.1: post directories\n const postDirs = this.fsHelper.getPostDirs(post);\n this.log('debug', 'Post directories:', postDirs);\n \n // Step 4.2: Check with status cache\n const statusCache = StatusCache.getInstance(this.config, postDirs.statusCache, this.logger);\n if (statusCache.validate(post, postDirs.root, this.config)) {\n this.log('info', `Skipped downloading post #${post.id}: already downloaded and nothing has changed since last download`);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.AlreadyDownloaded,\n skipMessage: 'Target already downloaded and nothing has changed since last download'\n });\n skippedRedundant++;\n continue;\n }\n \n this.log('info', `Download post #${post.id} (${post.title})`);\n \n // Step 4.3: Check whether we should download post\n // -- 4.3.1: Viewability\n if (!post.isViewable) {\n if (this.config.include.lockedContent) {\n this.log('warn', `Post #${post.id} is not viewable by current user`);\n }\n else {\n this.log('warn', `Skipped downloading post #${post.id}: not viewable by current user`);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.Inaccessible,\n skipMessage: 'Target is not viewable by current user'\n });\n skippedUnviewable++;\n continue;\n }\n }\n \n // -- 4.3.2: Config option 'include.postsWithMediaType'\n const postsWithMediaType = this.config.include.postsWithMediaType;\n if (postsWithMediaType !== 'any') {\n const hasAttachments = post.attachments.length > 0;\n const hasAudio = !!post.audio || !!post.audioPreview;\n const hasImages = post.images.length > 0;\n const hasVideo = !!post.video || !!post.videoPreview || !!(post.embed && post.embed.type === 'videoEmbed');\n \n let skip = false;\n if (postsWithMediaType === 'none') {\n skip = hasAttachments || hasAudio || hasImages || hasVideo;\n }\n else if (Array.isArray(postsWithMediaType)) {\n skip = !(\n (postsWithMediaType.includes('attachment') && hasAttachments) ||\n (postsWithMediaType.includes('audio') && hasAudio) ||\n (postsWithMediaType.includes('image') && hasImages) ||\n (postsWithMediaType.includes('video') && hasVideo));\n }\n \n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: unmet media type criteria`);\n this.log('debug', 'Match criteria failed:', `config.include.postsWithMediaType: ${JSON.stringify(postsWithMediaType)} <-> post #${post.id}:`, {\n hasAttachments,\n hasAudio,\n hasImages,\n hasVideo\n });\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.UnmetMediaTypeCriteria,\n skipMessage: 'Target does not meet media type criteria'\n });\n skippedUnmetMediaTypeCriteria++;\n continue;\n }\n }\n \n // -- 4.3.3: Config option 'include.postsInTier'\n const postsInTier = this.config.include.postsInTier;\n const isAnyTier = postsInTier === 'any' || postsInTier.includes('any');\n if (!isAnyTier) {\n const applicableTierIds = postsInTier.filter((id) => post.campaign?.rewards.find((r) => r.id === id));\n if (!post.campaign) {\n this.log('warn', 'config.include.postsInTier: ignored - post missing campaign info');\n }\n else {\n this.log('debug', 'config.include.postsInTier: applicable tier IDs:', applicableTierIds);\n }\n let skip = false;\n if (!post.campaign) {\n skip = false;\n }\n else if (applicableTierIds.length === 0) {\n skip = true;\n }\n else if (!post.tiers.find((tier) => tier.id === 'patrons')) {\n skip = applicableTierIds.every((id) => !post.tiers.find((tier) => tier.id === id));\n }\n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: not in tier`);\n this.log('debug', 'Match criteria failed:', `config.include.postsInTier: ${JSON.stringify(applicableTierIds)} <-> post #${post.id}:`, post.tiers);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.NotInTier,\n skipMessage: 'Target not in tier'\n });\n skippedNotInTier++;\n continue;\n }\n if (post.campaign) {\n this.log('debug', 'Match criteria OK:', `config.include.postsInTier: ${JSON.stringify(applicableTierIds)} <-> post #${post.id}:`, post.tiers);\n }\n }\n\n // -- 4.3.4: Config option 'include.postsPublished'\n const postsPublishedAfter = this.config.include.postsPublished.after;\n const postsPublishedBefore = this.config.include.postsPublished.before;\n if (postsPublishedAfter || postsPublishedBefore) {\n const targetPublishedAt = post.publishedAt;\n let parsedPublishedAt: Date | null = null;\n if (!targetPublishedAt) {\n this.log('warn', `config.include.postsPublished: ignored - post #${post.id} missing publish date`);\n }\n else {\n try {\n parsedPublishedAt = new Date(targetPublishedAt);\n }\n catch (error: any) {\n this.log('error', `Failed to parse publish date of post #${post.id} (\"${targetPublishedAt}\"): `, error);\n this.log('warn', `config.include.postsPublished: ignored - publish date of post #${post.id} could not be parsed`);\n }\n }\n let skip = false;\n if (parsedPublishedAt) {\n const isAfter = postsPublishedAfter ? parsedPublishedAt.getTime() >= postsPublishedAfter.valueOf().getTime() : true;\n const isBefore = postsPublishedBefore ? parsedPublishedAt.getTime() < postsPublishedBefore.valueOf().getTime() : true;\n skip = !isAfter || !isBefore;\n let eq: string | null = null;\n if (postsPublishedAfter && postsPublishedBefore) {\n eq = `${postsPublishedAfter.toString()} <= *${targetPublishedAt}* < ${postsPublishedBefore.toString()}`;\n }\n else if (postsPublishedAfter) {\n eq = `${postsPublishedAfter.toString()} <= *${targetPublishedAt}*`;\n }\n else if (postsPublishedBefore) {\n eq = `*${targetPublishedAt}* < ${postsPublishedBefore.toString()}`;\n }\n if (eq) {\n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: publish date out of range`);\n this.log('debug', `Publish date test failed for post #${post.id}: ${eq}`);\n }\n else {\n this.log('debug', `Publish date test OK for post #${post.id}: ${eq}`);\n }\n }\n if (skip) {\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.PublishDateOutOfRange,\n skipMessage: 'Publish date out of range'\n });\n skippedPublishDateOutOfRange++;\n continue;\n }\n }\n }\n\n \n // Step 4.4: Save post info\n if (this.config.include.contentInfo) {\n this.log('info', `Save post info #${post.id}`);\n this.emit('phaseBegin', { target: post, phase: 'saveInfo' });\n this.fsHelper.createDir(postDirs.info);\n // Post raw data might not be complete or consistent with other posts in the collection.\n // Fetch directly from API.\n // Strictly speaking, we should check for 'error' in results, but since it's not going to be fatal we'll just skip it.\n const { json: fetchedPostAPIData } = await this.commonFetchAPI(\n URLHelper.constructPostsAPIURL({\n postId: post.id,\n campaignId: post.campaign?.id\n }),\n signal\n );\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n // Save summary and raw json\n const summary = generatePostSummary(post);\n const summaryFile = path.resolve(postDirs.info, 'info.txt');\n const saveSummaryResult = await this.fsHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);\n this.logWriteTextFileResult(saveSummaryResult, post, 'post summary');\n \n const postRawFile = path.resolve(postDirs.info, 'post-api.json');\n const savePostRawResult = await this.fsHelper.writeTextFile(\n postRawFile, fetchedPostAPIData || post.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(savePostRawResult, post, 'post API data');\n this.emit('phaseEnd', { target: post, phase: 'saveInfo' });\n \n // (Downloading of info media items deferred to the next step)\n }\n \n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseBegin', { target: post, phase: 'saveMedia' });\n }\n \n // Step 4.5: save embed info\n if (post.embed && this.config.include.contentMedia) {\n this.log('info', `Save embed info of post #${post.id}`);\n this.fsHelper.createDir(postDirs.embed);\n const embedSummary = generatePostEmbedSummary(post.embed);\n let embedFilename;\n switch (post.embed.type) {\n case 'videoEmbed':\n embedFilename = 'embedded-video.txt';\n break;\n case 'linkEmbed':\n embedFilename = 'embedded-link.txt';\n break;\n default:\n embedFilename = 'embedded-unknown.txt';\n }\n const embedFile = path.resolve(postDirs.embed, embedFilename);\n const saveSummaryResult = await this.fsHelper.writeTextFile(embedFile, embedSummary, this.config.fileExistsAction.content);\n this.logWriteTextFileResult(saveSummaryResult, post, 'embed info');\n }\n \n // Step 4.6: create download tasks\n let createTaskErrorCount = 0;\n if (this.config.include.previewMedia ||\n this.config.include.contentMedia ||\n this.config.include.contentInfo) {\n \n const batchResult = await this.#createDownloadTaskBatchForPost(post, postDirs, signal);\n batch = batchResult.batch;\n createTaskErrorCount += batchResult.errorCount;\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n if (this.config.include.contentInfo) {\n const infoElements: Downloadable[] = [];\n if (post.coverImage) {\n infoElements.push(post.coverImage);\n }\n if (post.thumbnail) {\n infoElements.push(post.thumbnail);\n }\n if (infoElements.length > 0) {\n const { errorCount } = await this.addToDownloadTaskBatch(batch,\n signal,\n {\n target: infoElements,\n targetName: `post #${post.id} -> info elements`,\n destDir: postDirs.info,\n fileExistsAction: this.config.fileExistsAction.info\n }\n );\n createTaskErrorCount += errorCount;\n }\n }\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n batch.prestart();\n this.log('info', `Download batch created (#${batch.id}): ${batch.getTasks('pending').length} downloads pending`);\n this.emit('phaseBegin', { target: post, phase: 'batchDownload', batch });\n \n await batch.start();\n \n // Step 4.7: Update status cache\n statusCache.updateOnDownload(post, postDirs.root, batch.getTasks('error').length > 0 || createTaskErrorCount > 0, this.config);\n \n await batch.destroy();\n batch = null;\n this.emit('phaseEnd', { target: post, phase: 'batchDownload'});\n }\n \n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseEnd', { target: post, phase: 'saveMedia' });\n }\n \n downloaded++;\n this.emit('targetEnd', { target: post, isSkipped: false });\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n }\n }\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n // Done\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n if (postFetch.type === 'byUser') {\n this.log('info', `Done downloading posts by '${postFetch.vanity}'`);\n }\n else if (postFetch.type === 'byUserId') {\n this.log('info', `Done downloading posts by user #${postFetch.userId}`);\n }\n else if (postFetch.type === 'byCollection') {\n this.log('info', `Done downloading posts in collection #${postFetch.collectionId}`);\n }\n else {\n this.log('info', `Done downloading post #${postFetch.postId}`);\n }\n let endMessage = 'Done';\n if (this.#isFetchingMultiplePosts(postFetch)) {\n const skippedStrParts: string[] = [];\n if (skippedUnviewable) {\n skippedStrParts.push(`${skippedUnviewable} unviewable`);\n }\n if (skippedRedundant) {\n skippedStrParts.push(`${skippedRedundant} redundant`);\n }\n if (skippedUnmetMediaTypeCriteria) {\n skippedStrParts.push(`${skippedUnmetMediaTypeCriteria} with unmet media type criteria`);\n }\n if (skippedNotInTier) {\n skippedStrParts.push(`${skippedNotInTier} not in tier`);\n }\n if (skippedPublishDateOutOfRange) {\n skippedStrParts.push(`${skippedPublishDateOutOfRange} with publish date out of range`);\n }\n const skippedStr = skippedStrParts.length > 0 ? ` (skipped: ${skippedStrParts.join(', ')})` : '';\n endMessage = `Total ${downloaded} / ${postsFetcher.getTotal()} posts processed${skippedStr}`;\n this.log('info', endMessage);\n }\n this.emit('end', { aborted: false, message: endMessage });\n this.#startPromise = null;\n resolve();\n })();\n })\n .finally(() => {\n void (async () => {\n if (this.logger) {\n await this.logger.end();\n }\n this.#startPromise = null;\n })();\n });\n\n return this.#startPromise;\n }\n\n #isFetchingMultiplePosts(postFetch: DownloaderConfig<Post>['postFetch']): postFetch is DownloaderConfig<Post>['postFetch'] & { type: 'byUser' | 'byUserId' | 'byCollection' } {\n return postFetch.type === 'byUser' || postFetch.type === 'byUserId' || postFetch.type === 'byCollection';\n }\n\n async __getCampaign(signal?: AbortSignal) {\n let url: string | null;\n const postFetch = this.config.postFetch;\n switch (postFetch.type) {\n case 'byUser':\n url = URLHelper.constructCampaignPageURL({ vanity: postFetch.vanity });\n break;\n case 'byUserId':\n url = URLHelper.constructCampaignPageURL({ userId: postFetch.userId });\n break;\n default:\n url = null;\n }\n if (!url) {\n throw Error('Internal error: invalid config');\n }\n const postsFetcher = new PostsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n const { campaignId } = await postsFetcher.getInitialData(url);\n const postsParser = new PostParser(this.logger);\n const res = await this.fetchCampaign(campaignId);\n if (signal?.aborted) {\n throw new Error('Aborted');\n }\n if (res.error) {\n throw res.error;\n }\n return postsParser.parseCampaignAPIResponse(res.json);\n }\n\n #createDownloadTaskBatchForPost(post: Post, postDirs: ReturnType<typeof this.fsHelper['getPostDirs']>, signal?: AbortSignal) {\n\n const incPreview = this.config.include.previewMedia;\n const incContent = this.config.include.contentMedia;\n\n const __incPreview = (mediaType: 'audio' | 'video' | 'image') => {\n if (typeof incPreview === 'boolean') {\n return incPreview;\n }\n return incPreview.includes(mediaType);\n };\n\n const __incContent = (mediaType: 'audio' | 'video' | 'image' | 'attachment') => {\n if (typeof incContent === 'boolean') {\n return incContent;\n }\n return incContent.includes(mediaType);\n };\n\n const batchResult = this.createDownloadTaskBatch(\n `Post #${post.id} (${post.title})`,\n signal,\n\n __incPreview('audio') && post.audioPreview ? {\n target: [ post.audioPreview ],\n targetName: `post #${post.id} -> audio preview`,\n destDir: postDirs.audioPreview,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incPreview('video') && post.videoPreview ? {\n target: [ post.videoPreview ],\n targetName: `post #${post.id} -> video preview`,\n destDir: postDirs.videoPreview,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n /**\n * If post is not viewable by current user, its images will be\n * blurry and we should categorize them as image previews.\n */\n __incPreview('image') && post.images.length > 0 && !post.isViewable ? {\n target: post.images,\n targetName: `post #${post.id} -> image previews`,\n destDir: postDirs.imagePreviews,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('audio') && post.audio ? {\n target: [ post.audio ],\n targetName: `post #${post.id} -> audio`,\n destDir: postDirs.audio,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('video') && post.video ? {\n target: [ post.video ],\n targetName: `post #${post.id} -> video`,\n destDir: postDirs.video,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('image') && post.images.length > 0 && post.isViewable ? {\n target: post.images,\n targetName: `post #${post.id} -> images`,\n destDir: postDirs.images,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('attachment') && post.attachments.length > 0 ? {\n target: post.attachments,\n targetName: `post #${post.id} -> attachments`,\n destDir: postDirs.attachments,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('video') && post.embed &&\n (isYouTubeEmbed(post.embed) || DownloadTaskFactory.findEmbedDownloader(this.config.embedDownloaders, post.embed.provider)) ?\n {\n target: [ post.embed ],\n targetName: `post #${post.id} -> embedded ${post.embed.provider} video`,\n destDir: postDirs.embed,\n fileExistsAction: this.config.fileExistsAction.content\n } : null\n );\n\n return batchResult;\n }\n}\n"]}
1
+ {"version":3,"file":"PostDownloader.js","sourceRoot":"","sources":["../../src/downloaders/PostDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import URLHelper from '../utils/URLHelper.js';\nimport Downloader, { type DownloaderConfig, type DownloaderStartParams } from './Downloader.js';\nimport type DownloadTaskBatch from './task/DownloadTaskBatch.js';\nimport PostParser from '../parsers/PostParser.js';\nimport { type Post } from '../entities/Post.js';\nimport { type Downloadable, isYouTubeEmbed } from '../entities/Downloadable.js';\nimport StatusCache from './cache/StatusCache.js';\nimport { generatePostEmbedSummary, generatePostSummary } from './templates/PostInfo.js';\nimport path from 'path';\nimport { TargetSkipReason } from './DownloaderEvent.js';\nimport DownloadTaskFactory from './task/DownloadTaskFactory.js';\nimport PostsFetcher from './PostsFetcher.js';\n\nexport default class PostDownloader extends Downloader<Post> {\n\n static version = '1.1.1';\n\n name = 'PostDownloader';\n\n #startPromise: Promise<void> | null = null;\n\n start(params?: DownloaderStartParams): Promise<void> {\n\n if (this.#startPromise) {\n throw Error('Downloader already running');\n }\n\n this.#startPromise = new Promise<void>((resolve) => {\n void (async () => {\n const { signal } = params || {};\n const postFetch = this.config.postFetch;\n let batch: DownloadTaskBatch | null = null;\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n const abortHandler = () => {\n void (async () => {\n this.log('info', 'Abort signal received');\n if (batch) {\n await batch.abort();\n }\n })();\n };\n if (signal) {\n signal.addEventListener('abort', abortHandler, { once: true });\n }\n \n if (postFetch.type === 'byUser') {\n this.log('info', `Targeting posts by '${postFetch.vanity}'`);\n }\n else if (postFetch.type === 'byUserId') {\n this.log('info', `Targeting posts by user #${postFetch.userId}`);\n }\n else if (postFetch.type === 'byCollection') {\n this.log('info', `Targeting posts in collection #${postFetch.collectionId}`);\n }\n else { // Single\n this.log('info', `Targeting post #${postFetch.postId}`);\n }\n if (this.#isFetchingMultiplePosts(postFetch) && postFetch.filters) {\n const filterStr = Object.entries(postFetch.filters).map(([ key, value ]) => `${key}=${value}`).join('; ');\n if (filterStr) {\n this.log('info', `Filters: ${filterStr}`);\n }\n }\n \n // Step 1: Get posts (if by user) or target post\n const postsFetcher = new PostsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n postsFetcher.on('statusChange', ({current}) => {\n if (current.status === 'running') {\n this.emit('fetchBegin', { targetType: postsFetcher.getTargetType() });\n }\n });\n postsFetcher.begin();\n \n // Step 2: download posts in each fetched collection\n let downloaded = 0;\n let skippedUnviewable = 0;\n let skippedRedundant = 0;\n let skippedUnmetMediaTypeCriteria = 0;\n let skippedNotInTier = 0;\n let skippedPublishDateOutOfRange = 0;\n let campaignSaved = false;\n const postsParser = new PostParser(this.logger);\n while (postsFetcher.hasNext()) {\n const { collection, aborted, error } = await postsFetcher.next();\n if (!collection || aborted) {\n break;\n }\n if (!collection && error) {\n this.emit('end', { aborted: false, error, message: 'PostsFetcher error' });\n resolve();\n return;\n }\n if (!campaignSaved && collection.posts[0]?.campaign) {\n await this.saveCampaignInfo(collection.posts[0].campaign, signal);\n campaignSaved = true;\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n }\n \n for (const _post of collection.posts) {\n \n let post = _post;\n \n if (this.#isFetchingMultiplePosts(postFetch)) {\n // Refresh to ensure media links have not expired\n this.log('debug', `Refresh post #${_post.id}`);\n const postURL = URLHelper.constructPostsAPIURL({ postId: _post.id });\n const { json } = await this.commonFetchAPI(postURL, signal);\n let refreshed: Post | null = null;\n if (json) {\n refreshed = postsParser.parsePostsAPIResponse(json, postURL).posts[0] || null;\n if (!refreshed) {\n this.log('warn', `Refreshed post #${_post.id} but got empty value - going to use existing data`);\n }\n else if (refreshed.id !== _post.id) {\n this.log('warn', `Refreshed post #${_post.id} but ID mismatch - going to use existing data`);\n refreshed = null;\n }\n }\n else if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n else {\n this.log('warn', `Failed to refresh post #${_post.id} - going to use existing data`);\n }\n if (refreshed) {\n this.log('debug', `Use refreshed post data #${refreshed.id}`);\n post = refreshed;\n }\n }\n \n this.emit('targetBegin', { target: post });\n \n // Step 4.1: post directories\n const postDirs = this.fsHelper.getPostDirs(post);\n this.log('debug', 'Post directories:', postDirs);\n \n // Step 4.2: Check with status cache\n const statusCache = StatusCache.getInstance(this.config, postDirs.statusCache, this.logger);\n if (statusCache.validate(post, postDirs.root, this.config)) {\n this.log('info', `Skipped downloading post #${post.id}: already downloaded and nothing has changed since last download`);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.AlreadyDownloaded,\n skipMessage: 'Target already downloaded and nothing has changed since last download'\n });\n skippedRedundant++;\n continue;\n }\n \n this.log('info', `Download post #${post.id} (${post.title})`);\n \n // Step 4.3: Check whether we should download post\n // -- 4.3.1: Viewability\n if (!post.isViewable) {\n if (this.config.include.lockedContent) {\n this.log('warn', `Post #${post.id} is not viewable by current user`);\n }\n else {\n this.log('warn', `Skipped downloading post #${post.id}: not viewable by current user`);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.Inaccessible,\n skipMessage: 'Target is not viewable by current user'\n });\n skippedUnviewable++;\n continue;\n }\n }\n \n // -- 4.3.2: Config option 'include.postsWithMediaType'\n const postsWithMediaType = this.config.include.postsWithMediaType;\n if (postsWithMediaType !== 'any') {\n const hasAttachments = post.attachments.length > 0;\n const hasAudio = !!post.audio || !!post.audioPreview;\n const hasImages = post.images.length > 0;\n const hasVideo = !!post.video || !!post.videoPreview || !!(post.embed && (post.embed.type === 'videoEmbed' || isYouTubeEmbed(post.embed)));\n \n let skip = false;\n if (postsWithMediaType === 'none') {\n skip = hasAttachments || hasAudio || hasImages || hasVideo;\n }\n else if (Array.isArray(postsWithMediaType)) {\n skip = !(\n (postsWithMediaType.includes('attachment') && hasAttachments) ||\n (postsWithMediaType.includes('audio') && hasAudio) ||\n (postsWithMediaType.includes('image') && hasImages) ||\n (postsWithMediaType.includes('video') && hasVideo));\n }\n \n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: unmet media type criteria`);\n this.log('debug', 'Match criteria failed:', `config.include.postsWithMediaType: ${JSON.stringify(postsWithMediaType)} <-> post #${post.id}:`, {\n hasAttachments,\n hasAudio,\n hasImages,\n hasVideo\n });\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.UnmetMediaTypeCriteria,\n skipMessage: 'Target does not meet media type criteria'\n });\n skippedUnmetMediaTypeCriteria++;\n continue;\n }\n }\n \n // -- 4.3.3: Config option 'include.postsInTier'\n const postsInTier = this.config.include.postsInTier;\n const isAnyTier = postsInTier === 'any' || postsInTier.includes('any');\n if (!isAnyTier) {\n const applicableTierIds = postsInTier.filter((id) => post.campaign?.rewards.find((r) => r.id === id));\n if (!post.campaign) {\n this.log('warn', 'config.include.postsInTier: ignored - post missing campaign info');\n }\n else {\n this.log('debug', 'config.include.postsInTier: applicable tier IDs:', applicableTierIds);\n }\n let skip = false;\n if (!post.campaign) {\n skip = false;\n }\n else if (applicableTierIds.length === 0) {\n skip = true;\n }\n else if (!post.tiers.find((tier) => tier.id === 'patrons')) {\n skip = applicableTierIds.every((id) => !post.tiers.find((tier) => tier.id === id));\n }\n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: not in tier`);\n this.log('debug', 'Match criteria failed:', `config.include.postsInTier: ${JSON.stringify(applicableTierIds)} <-> post #${post.id}:`, post.tiers);\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.NotInTier,\n skipMessage: 'Target not in tier'\n });\n skippedNotInTier++;\n continue;\n }\n if (post.campaign) {\n this.log('debug', 'Match criteria OK:', `config.include.postsInTier: ${JSON.stringify(applicableTierIds)} <-> post #${post.id}:`, post.tiers);\n }\n }\n\n // -- 4.3.4: Config option 'include.postsPublished'\n const postsPublishedAfter = this.config.include.postsPublished.after;\n const postsPublishedBefore = this.config.include.postsPublished.before;\n if (postsPublishedAfter || postsPublishedBefore) {\n const targetPublishedAt = post.publishedAt;\n let parsedPublishedAt: Date | null = null;\n if (!targetPublishedAt) {\n this.log('warn', `config.include.postsPublished: ignored - post #${post.id} missing publish date`);\n }\n else {\n try {\n parsedPublishedAt = new Date(targetPublishedAt);\n }\n catch (error: any) {\n this.log('error', `Failed to parse publish date of post #${post.id} (\"${targetPublishedAt}\"): `, error);\n this.log('warn', `config.include.postsPublished: ignored - publish date of post #${post.id} could not be parsed`);\n }\n }\n let skip = false;\n if (parsedPublishedAt) {\n const isAfter = postsPublishedAfter ? parsedPublishedAt.getTime() >= postsPublishedAfter.valueOf().getTime() : true;\n const isBefore = postsPublishedBefore ? parsedPublishedAt.getTime() < postsPublishedBefore.valueOf().getTime() : true;\n skip = !isAfter || !isBefore;\n let eq: string | null = null;\n if (postsPublishedAfter && postsPublishedBefore) {\n eq = `${postsPublishedAfter.toString()} <= *${targetPublishedAt}* < ${postsPublishedBefore.toString()}`;\n }\n else if (postsPublishedAfter) {\n eq = `${postsPublishedAfter.toString()} <= *${targetPublishedAt}*`;\n }\n else if (postsPublishedBefore) {\n eq = `*${targetPublishedAt}* < ${postsPublishedBefore.toString()}`;\n }\n if (eq) {\n if (skip) {\n this.log('warn', `Skipped downloading post #${post.id}: publish date out of range`);\n this.log('debug', `Publish date test failed for post #${post.id}: ${eq}`);\n }\n else {\n this.log('debug', `Publish date test OK for post #${post.id}: ${eq}`);\n }\n }\n if (skip) {\n this.emit('targetEnd', {\n target: post,\n isSkipped: true,\n skipReason: TargetSkipReason.PublishDateOutOfRange,\n skipMessage: 'Publish date out of range'\n });\n skippedPublishDateOutOfRange++;\n continue;\n }\n }\n }\n\n \n // Step 4.4: Save post info\n if (this.config.include.contentInfo) {\n this.log('info', `Save post info #${post.id}`);\n this.emit('phaseBegin', { target: post, phase: 'saveInfo' });\n this.fsHelper.createDir(postDirs.info);\n // Post raw data might not be complete or consistent with other posts in the collection.\n // Fetch directly from API.\n // Strictly speaking, we should check for 'error' in results, but since it's not going to be fatal we'll just skip it.\n const { json: fetchedPostAPIData } = await this.commonFetchAPI(\n URLHelper.constructPostsAPIURL({\n postId: post.id,\n campaignId: post.campaign?.id\n }),\n signal\n );\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n // Save summary and raw json\n const summary = generatePostSummary(post);\n const summaryFile = path.resolve(postDirs.info, 'info.txt');\n const saveSummaryResult = await this.fsHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);\n this.logWriteTextFileResult(saveSummaryResult, post, 'post summary');\n \n const postRawFile = path.resolve(postDirs.info, 'post-api.json');\n const savePostRawResult = await this.fsHelper.writeTextFile(\n postRawFile, fetchedPostAPIData || post.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(savePostRawResult, post, 'post API data');\n this.emit('phaseEnd', { target: post, phase: 'saveInfo' });\n \n // (Downloading of info media items deferred to the next step)\n }\n \n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseBegin', { target: post, phase: 'saveMedia' });\n }\n \n // Step 4.5: save embed info\n if (post.embed && this.config.include.contentMedia) {\n this.log('info', `Save embed info of post #${post.id}`);\n this.fsHelper.createDir(postDirs.embed);\n const embedSummary = generatePostEmbedSummary(post.embed);\n let embedFilename;\n switch (post.embed.type) {\n case 'videoEmbed':\n embedFilename = 'embedded-video.txt';\n break;\n case 'linkEmbed':\n embedFilename = 'embedded-link.txt';\n break;\n default:\n embedFilename = 'embedded-unknown.txt';\n }\n const embedFile = path.resolve(postDirs.embed, embedFilename);\n const saveSummaryResult = await this.fsHelper.writeTextFile(embedFile, embedSummary, this.config.fileExistsAction.content);\n this.logWriteTextFileResult(saveSummaryResult, post, 'embed info');\n }\n \n // Step 4.6: create download tasks\n let createTaskErrorCount = 0;\n if (this.config.include.previewMedia ||\n this.config.include.contentMedia ||\n this.config.include.contentInfo) {\n \n const batchResult = await this.#createDownloadTaskBatchForPost(post, postDirs, signal);\n batch = batchResult.batch;\n createTaskErrorCount += batchResult.errorCount;\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n if (this.config.include.contentInfo) {\n const infoElements: Downloadable[] = [];\n if (post.coverImage) {\n infoElements.push(post.coverImage);\n }\n if (post.thumbnail) {\n infoElements.push(post.thumbnail);\n }\n if (infoElements.length > 0) {\n const { errorCount } = await this.addToDownloadTaskBatch(batch,\n signal,\n {\n target: infoElements,\n targetName: `post #${post.id} -> info elements`,\n destDir: postDirs.info,\n fileExistsAction: this.config.fileExistsAction.info\n }\n );\n createTaskErrorCount += errorCount;\n }\n }\n\n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n batch.prestart();\n this.log('info', `Download batch created (#${batch.id}): ${batch.getTasks('pending').length} downloads pending`);\n this.emit('phaseBegin', { target: post, phase: 'batchDownload', batch });\n \n await batch.start();\n \n // Step 4.7: Update status cache\n statusCache.updateOnDownload(post, postDirs.root, batch.getTasks('error').length > 0 || createTaskErrorCount > 0, this.config);\n \n await batch.destroy();\n batch = null;\n this.emit('phaseEnd', { target: post, phase: 'batchDownload'});\n }\n \n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseEnd', { target: post, phase: 'saveMedia' });\n }\n \n downloaded++;\n this.emit('targetEnd', { target: post, isSkipped: false });\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n }\n }\n \n if (this.checkAbortSignal(signal, resolve)) {\n return;\n }\n \n // Done\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n if (postFetch.type === 'byUser') {\n this.log('info', `Done downloading posts by '${postFetch.vanity}'`);\n }\n else if (postFetch.type === 'byUserId') {\n this.log('info', `Done downloading posts by user #${postFetch.userId}`);\n }\n else if (postFetch.type === 'byCollection') {\n this.log('info', `Done downloading posts in collection #${postFetch.collectionId}`);\n }\n else {\n this.log('info', `Done downloading post #${postFetch.postId}`);\n }\n let endMessage = 'Done';\n if (this.#isFetchingMultiplePosts(postFetch)) {\n const skippedStrParts: string[] = [];\n if (skippedUnviewable) {\n skippedStrParts.push(`${skippedUnviewable} unviewable`);\n }\n if (skippedRedundant) {\n skippedStrParts.push(`${skippedRedundant} redundant`);\n }\n if (skippedUnmetMediaTypeCriteria) {\n skippedStrParts.push(`${skippedUnmetMediaTypeCriteria} with unmet media type criteria`);\n }\n if (skippedNotInTier) {\n skippedStrParts.push(`${skippedNotInTier} not in tier`);\n }\n if (skippedPublishDateOutOfRange) {\n skippedStrParts.push(`${skippedPublishDateOutOfRange} with publish date out of range`);\n }\n const skippedStr = skippedStrParts.length > 0 ? ` (skipped: ${skippedStrParts.join(', ')})` : '';\n endMessage = `Total ${downloaded} / ${postsFetcher.getTotal()} posts processed${skippedStr}`;\n this.log('info', endMessage);\n }\n this.emit('end', { aborted: false, message: endMessage });\n this.#startPromise = null;\n resolve();\n })();\n })\n .finally(() => {\n void (async () => {\n if (this.logger) {\n await this.logger.end();\n }\n this.#startPromise = null;\n })();\n });\n\n return this.#startPromise;\n }\n\n #isFetchingMultiplePosts(postFetch: DownloaderConfig<Post>['postFetch']): postFetch is DownloaderConfig<Post>['postFetch'] & { type: 'byUser' | 'byUserId' | 'byCollection' } {\n return postFetch.type === 'byUser' || postFetch.type === 'byUserId' || postFetch.type === 'byCollection';\n }\n\n async __getCampaign(signal?: AbortSignal) {\n let url: string | null;\n const postFetch = this.config.postFetch;\n switch (postFetch.type) {\n case 'byUser':\n url = URLHelper.constructCampaignPageURL({ vanity: postFetch.vanity });\n break;\n case 'byUserId':\n url = URLHelper.constructCampaignPageURL({ userId: postFetch.userId });\n break;\n default:\n url = null;\n }\n if (!url) {\n throw Error('Internal error: invalid config');\n }\n const postsFetcher = new PostsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n const { campaignId } = await postsFetcher.getInitialData(url);\n const postsParser = new PostParser(this.logger);\n const res = await this.fetchCampaign(campaignId);\n if (signal?.aborted) {\n throw new Error('Aborted');\n }\n if (res.error) {\n throw res.error;\n }\n return postsParser.parseCampaignAPIResponse(res.json);\n }\n\n #createDownloadTaskBatchForPost(post: Post, postDirs: ReturnType<typeof this.fsHelper['getPostDirs']>, signal?: AbortSignal) {\n\n const incPreview = this.config.include.previewMedia;\n const incContent = this.config.include.contentMedia;\n\n const __incPreview = (mediaType: 'audio' | 'video' | 'image') => {\n if (typeof incPreview === 'boolean') {\n return incPreview;\n }\n return incPreview.includes(mediaType);\n };\n\n const __incContent = (mediaType: 'audio' | 'video' | 'image' | 'attachment') => {\n if (typeof incContent === 'boolean') {\n return incContent;\n }\n return incContent.includes(mediaType);\n };\n\n const __getEmbedTask = () => {\n if (!post.embed) {\n return null;\n }\n\n const hasEmbedDownloader = DownloadTaskFactory.findEmbedDownloader(this.config.embedDownloaders, post.embed.provider);\n const isDownloadableVideo = isYouTubeEmbed(post.embed) || (post.embed.type === 'videoEmbed' && hasEmbedDownloader);\n const isDownloadableOther = !isYouTubeEmbed(post.embed) && post.embed.type !== 'videoEmbed' && hasEmbedDownloader;\n \n if ((isDownloadableVideo && __incContent('video')) || isDownloadableOther) {\n const embedType = post.embed.type === 'videoEmbed' ? ' video' : post.embed.type === 'linkEmbed' ? ' link' : '';\n return {\n target: [ post.embed ],\n targetName: `post #${post.id} -> embedded ${post.embed.provider}${embedType}`,\n destDir: postDirs.embed,\n fileExistsAction: this.config.fileExistsAction.content\n };\n }\n \n return null;\n }\n\n const batchResult = this.createDownloadTaskBatch(\n `Post #${post.id} (${post.title})`,\n signal,\n\n __incPreview('audio') && post.audioPreview ? {\n target: [ post.audioPreview ],\n targetName: `post #${post.id} -> audio preview`,\n destDir: postDirs.audioPreview,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incPreview('video') && post.videoPreview ? {\n target: [ post.videoPreview ],\n targetName: `post #${post.id} -> video preview`,\n destDir: postDirs.videoPreview,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n /**\n * If post is not viewable by current user, its images will be\n * blurry and we should categorize them as image previews.\n */\n __incPreview('image') && post.images.length > 0 && !post.isViewable ? {\n target: post.images,\n targetName: `post #${post.id} -> image previews`,\n destDir: postDirs.imagePreviews,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('audio') && post.audio ? {\n target: [ post.audio ],\n targetName: `post #${post.id} -> audio`,\n destDir: postDirs.audio,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('video') && post.video ? {\n target: [ post.video ],\n targetName: `post #${post.id} -> video`,\n destDir: postDirs.video,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('image') && post.images.length > 0 && post.isViewable ? {\n target: post.images,\n targetName: `post #${post.id} -> images`,\n destDir: postDirs.images,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __incContent('attachment') && post.attachments.length > 0 ? {\n target: post.attachments,\n targetName: `post #${post.id} -> attachments`,\n destDir: postDirs.attachments,\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n __getEmbedTask()\n );\n\n return batchResult;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DownloadTaskFactory.d.ts","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTaskFactory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAElD,OAAO,EAAC,KAAK,qBAAqB,EAAC,MAAM,mBAAmB,CAAC;AAC7D,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,KAAK,YAAY,EAA2B,MAAM,gCAAgC,CAAC;AAC5F,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AAGxD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,UAAU,MAAM,YAAY,CAAC;AAqCzC,MAAM,CAAC,OAAO,OAAO,mBAAmB;;WAEzB,sBAAsB,CAAC,MAAM,EAAE;QAC1C,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,SAAS,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;QACzC,OAAO,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB;IAyMD,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAG5E"}
1
+ {"version":3,"file":"DownloadTaskFactory.d.ts","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTaskFactory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAElD,OAAO,EAAC,KAAK,qBAAqB,EAAC,MAAM,mBAAmB,CAAC;AAC7D,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,KAAK,YAAY,EAA2B,MAAM,gCAAgC,CAAC;AAC5F,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAC;AAGxD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,UAAU,MAAM,YAAY,CAAC;AAqCzC,MAAM,CAAC,OAAO,OAAO,mBAAmB;;WAyEzB,sBAAsB,CAAC,MAAM,EAAE;QAC1C,MAAM,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,SAAS,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;QACzC,OAAO,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB;IA4HD,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAG5E"}
@@ -3,7 +3,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
3
3
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
- var _a, _DownloadTaskFactory_pickVariant;
6
+ var _a, _DownloadTaskFactory_getSrcURLs, _DownloadTaskFactory_pickVariant;
7
7
  import path from 'path';
8
8
  import MediaFilenameResolver from '../../utils/MediaFilenameResolver.js';
9
9
  import DownloadTask from './DownloadTask.js';
@@ -47,106 +47,32 @@ class DownloadTaskFactory {
47
47
  const embedDownloaders = config.embedDownloaders;
48
48
  const destFilenameFormat = config.filenameFormat.media;
49
49
  const downloadAllVariants = config.include.allMediaVariants;
50
- const __getSrcURLs = () => {
51
- if (item.type === 'image') {
52
- let urls = {};
53
- let priority = [];
54
- switch (item.imageType) {
55
- case 'single':
56
- return {
57
- [NULL_VARIANT]: item.imageURL
58
- };
59
- case 'default':
60
- urls = {
61
- ...item.imageURLs,
62
- download: item.downloadURL
63
- };
64
- priority = DEFAULT_IMAGE_URL_PRIORITY;
65
- break;
66
- case 'campaignCoverPhoto':
67
- urls = item.imageURLs;
68
- priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;
69
- break;
70
- case 'postCoverImage':
71
- urls = item.imageURLs;
72
- priority = POST_COVER_IMAGE_URL_PRIORITY;
73
- break;
74
- case 'postThumbnail':
75
- urls = item.imageURLs;
76
- priority = POST_THUMBNAIL_URL_PRIORITY;
77
- break;
78
- }
79
- if (downloadAllVariants) {
80
- return urls;
81
- }
82
- return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
83
- }
84
- else if (item.type === 'video') {
85
- let videoVariantName = 'display';
86
- if (item.size.width && item.size.height) {
87
- videoVariantName = `${item.size.width}x${item.size.height}`;
88
- }
89
- const urls = {
90
- download: item.downloadURL,
91
- [videoVariantName]: item.displayURLs.video
92
- };
93
- if (downloadAllVariants) {
94
- return urls;
95
- }
96
- const priority = ['download', videoVariantName];
97
- return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
98
- }
99
- else if (item.type === 'audio') {
100
- return {
101
- [NULL_VARIANT]: item.url
102
- };
103
- }
104
- else if (item.type === 'file') {
105
- return {
106
- [NULL_VARIANT]: item.downloadURL
107
- };
108
- }
109
- else if (item.type === 'dummy') {
110
- return item.srcURLs;
111
- }
112
- else if (item.type === 'attachment') {
113
- return {
114
- [NULL_VARIANT]: item.downloadURL
115
- };
116
- }
117
- else if (item.type === 'videoEmbed') {
118
- return {
119
- [NULL_VARIANT]: item.url
120
- };
121
- }
122
- return {};
123
- };
124
- const srcURLs = __getSrcURLs();
125
50
  const tasks = [];
126
- for (const [variant, url] of Object.entries(srcURLs)) {
127
- if (isEmbed(item)) {
128
- // Check if external downloader configured for embed item
129
- const embedDownloader = this.findEmbedDownloader(embedDownloaders, item.provider);
130
- if (embedDownloader) {
131
- const task = ExternalDownloaderTask.fromEmbedDownloader(config, embedDownloader, item, destDir, callbacks || null, logger);
132
- if (task) {
133
- tasks.push(task);
134
- }
135
- }
136
- // Use our own implementation if no external downloader configured for YT embeds
137
- else if (item.type === 'videoEmbed' && isYouTubeEmbed(item) && url) {
138
- tasks.push(await DownloadTask.create(YouTubeDownloadTask, {
139
- config,
140
- src: url,
141
- destDir,
142
- fileExistsAction,
143
- srcEntity: item,
144
- callbacks: callbacks || null,
145
- logger
146
- }, limiter, signal));
51
+ if (isEmbed(item)) {
52
+ // Check if external downloader configured for embed item
53
+ const embedDownloader = this.findEmbedDownloader(embedDownloaders, item.provider);
54
+ if (embedDownloader) {
55
+ const task = ExternalDownloaderTask.fromEmbedDownloader(config, embedDownloader, item, destDir, callbacks || null, logger);
56
+ if (task) {
57
+ tasks.push(task);
147
58
  }
148
59
  }
149
- else {
60
+ // Use our own implementation if no external downloader configured for YT embeds
61
+ else if (isYouTubeEmbed(item) && item.url) {
62
+ tasks.push(await DownloadTask.create(YouTubeDownloadTask, {
63
+ config,
64
+ src: item.url,
65
+ destDir,
66
+ fileExistsAction,
67
+ srcEntity: item,
68
+ callbacks: callbacks || null,
69
+ logger
70
+ }, limiter, signal));
71
+ }
72
+ }
73
+ else {
74
+ const srcURLs = __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_getSrcURLs).call(this, item, downloadAllVariants);
75
+ for (const [variant, url] of Object.entries(srcURLs)) {
150
76
  const destFilenameResolver = new MediaFilenameResolver(item, url, destFilenameFormat, variant !== NULL_VARIANT ? variant : null, downloadAllVariants);
151
77
  if (url) {
152
78
  tasks.push(await DownloadTask.create(FetcherDownloadTask, {
@@ -207,7 +133,75 @@ class DownloadTaskFactory {
207
133
  return edl.find((dl) => dl.provider.toLowerCase() === provider?.trim().toLowerCase());
208
134
  }
209
135
  }
210
- _a = DownloadTaskFactory, _DownloadTaskFactory_pickVariant = function _DownloadTaskFactory_pickVariant(urls, priority) {
136
+ _a = DownloadTaskFactory, _DownloadTaskFactory_getSrcURLs = function _DownloadTaskFactory_getSrcURLs(item, allVariants) {
137
+ if (item.type === 'image') {
138
+ let urls = {};
139
+ let priority = [];
140
+ switch (item.imageType) {
141
+ case 'single':
142
+ return {
143
+ [NULL_VARIANT]: item.imageURL
144
+ };
145
+ case 'default':
146
+ urls = {
147
+ ...item.imageURLs,
148
+ download: item.downloadURL
149
+ };
150
+ priority = DEFAULT_IMAGE_URL_PRIORITY;
151
+ break;
152
+ case 'campaignCoverPhoto':
153
+ urls = item.imageURLs;
154
+ priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;
155
+ break;
156
+ case 'postCoverImage':
157
+ urls = item.imageURLs;
158
+ priority = POST_COVER_IMAGE_URL_PRIORITY;
159
+ break;
160
+ case 'postThumbnail':
161
+ urls = item.imageURLs;
162
+ priority = POST_THUMBNAIL_URL_PRIORITY;
163
+ break;
164
+ }
165
+ if (allVariants) {
166
+ return urls;
167
+ }
168
+ return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
169
+ }
170
+ else if (item.type === 'video') {
171
+ let videoVariantName = 'display';
172
+ if (item.size.width && item.size.height) {
173
+ videoVariantName = `${item.size.width}x${item.size.height}`;
174
+ }
175
+ const urls = {
176
+ download: item.downloadURL,
177
+ [videoVariantName]: item.displayURLs.video
178
+ };
179
+ if (allVariants) {
180
+ return urls;
181
+ }
182
+ const priority = ['download', videoVariantName];
183
+ return __classPrivateFieldGet(this, _a, "m", _DownloadTaskFactory_pickVariant).call(this, urls, priority);
184
+ }
185
+ else if (item.type === 'audio') {
186
+ return {
187
+ [NULL_VARIANT]: item.url
188
+ };
189
+ }
190
+ else if (item.type === 'file') {
191
+ return {
192
+ [NULL_VARIANT]: item.downloadURL
193
+ };
194
+ }
195
+ else if (item.type === 'dummy') {
196
+ return item.srcURLs;
197
+ }
198
+ else if (item.type === 'attachment') {
199
+ return {
200
+ [NULL_VARIANT]: item.downloadURL
201
+ };
202
+ }
203
+ return {};
204
+ }, _DownloadTaskFactory_pickVariant = function _DownloadTaskFactory_pickVariant(urls, priority) {
211
205
  for (const variant of priority) {
212
206
  if (urls[variant]) {
213
207
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"DownloadTaskFactory.js","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTaskFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type DummyMediaItem } from '../../entities/MediaItem.js';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport MediaFilenameResolver from '../../utils/MediaFilenameResolver.js';\nimport {type DownloadTaskCallbacks} from './DownloadTask.js';\nimport DownloadTask from './DownloadTask.js';\nimport FetcherDownloadTask from './FetcherDownloadTask.js';\nimport { type Downloadable, isEmbed, isYouTubeEmbed } from '../../entities/Downloadable.js';\nimport { type EmbedDownloader, type FileExistsAction } from '../DownloaderOptions.js';\nimport type Logger from '../../utils/logging/Logger.js';\nimport YouTubeDownloadTask from './YouTubeDownloadTask.js';\nimport ExternalDownloaderTask from './ExternalDownloaderTask.js';\nimport { type DownloaderConfig } from '../Downloader.js';\nimport type Bottleneck from 'bottleneck';\n\nconst DEFAULT_IMAGE_URL_PRIORITY = [\n 'original',\n 'default',\n 'download',\n 'defaultSmall',\n 'thumbnailLarge',\n 'thumbnail',\n 'thumbnailSmall'\n];\n\nconst CAMPAIGN_COVER_PHOTO_URL_PRIORITY = [\n 'xlarge',\n 'large',\n 'medium',\n 'small',\n 'xsmall'\n];\n\nconst POST_COVER_IMAGE_URL_PRIORITY = [\n 'large',\n 'default',\n 'thumb',\n 'thumbSquareLarge',\n 'thumbSquare'\n];\n\nconst POST_THUMBNAIL_URL_PRIORITY = [\n 'large2',\n 'default',\n 'large',\n 'square'\n];\n\nconst NULL_VARIANT = '/*NULL*/';\n\nexport default class DownloadTaskFactory {\n\n static async createFromDownloadable(params: {\n config: DownloaderConfig<any>,\n item: Downloadable,\n destDir: string,\n fetcher: Fetcher,\n fileExistsAction: FileExistsAction,\n callbacks?: DownloadTaskCallbacks | null,\n limiter: Bottleneck,\n signal?: AbortSignal,\n logger?: Logger | null;\n }) {\n\n const {\n config,\n item,\n destDir,\n fetcher,\n fileExistsAction,\n callbacks,\n limiter,\n signal,\n logger\n } = params;\n\n const embedDownloaders = config.embedDownloaders;\n const destFilenameFormat = config.filenameFormat.media;\n const downloadAllVariants = config.include.allMediaVariants;\n\n const __getSrcURLs = () => {\n if (item.type === 'image') {\n let urls: Record<string, string | null> = {};\n let priority: string[] = [];\n switch (item.imageType) {\n case 'single':\n return {\n [NULL_VARIANT]: item.imageURL\n };\n case 'default':\n urls = {\n ...item.imageURLs,\n download: item.downloadURL\n };\n priority = DEFAULT_IMAGE_URL_PRIORITY;\n break;\n case 'campaignCoverPhoto':\n urls = item.imageURLs;\n priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;\n break;\n case 'postCoverImage':\n urls = item.imageURLs;\n priority = POST_COVER_IMAGE_URL_PRIORITY;\n break;\n case 'postThumbnail':\n urls = item.imageURLs;\n priority = POST_THUMBNAIL_URL_PRIORITY;\n break;\n }\n if (downloadAllVariants) {\n return urls;\n }\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'video') {\n let videoVariantName = 'display';\n if (item.size.width && item.size.height) {\n videoVariantName = `${item.size.width}x${item.size.height}`;\n }\n const urls = {\n download: item.downloadURL,\n [videoVariantName]: item.displayURLs.video\n };\n if (downloadAllVariants) {\n return urls;\n }\n const priority = [ 'download', videoVariantName ];\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'audio') {\n return {\n [NULL_VARIANT]: item.url\n };\n }\n else if (item.type === 'file') {\n return {\n [NULL_VARIANT]: item.downloadURL\n };\n }\n else if (item.type === 'dummy') {\n return item.srcURLs;\n }\n else if (item.type === 'attachment') {\n return {\n [NULL_VARIANT]: item.downloadURL\n };\n }\n else if (item.type === 'videoEmbed') {\n return {\n [NULL_VARIANT]: item.url\n };\n }\n return {};\n };\n\n\n const srcURLs = __getSrcURLs();\n\n const tasks: DownloadTask[] = [];\n for (const [ variant, url ] of Object.entries(srcURLs)) {\n if (isEmbed(item)) {\n // Check if external downloader configured for embed item\n const embedDownloader = this.findEmbedDownloader(embedDownloaders, item.provider);\n if (embedDownloader) {\n const task = ExternalDownloaderTask.fromEmbedDownloader(config, embedDownloader, item, destDir, callbacks || null, logger);\n if (task) {\n tasks.push(task);\n }\n }\n // Use our own implementation if no external downloader configured for YT embeds\n else if (item.type === 'videoEmbed' && isYouTubeEmbed(item) && url) {\n tasks.push(await DownloadTask.create(YouTubeDownloadTask, {\n config,\n src: url,\n destDir,\n fileExistsAction,\n srcEntity: item,\n callbacks: callbacks || null,\n logger\n },\n limiter,\n signal));\n }\n }\n else {\n const destFilenameResolver = \n new MediaFilenameResolver(item, url, destFilenameFormat,\n variant !== NULL_VARIANT ? variant : null, downloadAllVariants);\n\n if (url) {\n tasks.push(await DownloadTask.create(FetcherDownloadTask, {\n config,\n fetcher,\n src: url,\n destDir,\n destFilenameResolver,\n fileExistsAction,\n srcEntity: item,\n callbacks: callbacks || null,\n logger\n },\n limiter,\n signal));\n }\n }\n }\n\n // Create a DownloadTask backed by a DummyMediaItem for video thumbnail\n if (item.type === 'video' && item.displayURLs.thumbnail) {\n let filename: string | null = null;\n if (item.filename) {\n // Video file extension not applicable to thumbnail, so strip it.\n filename = path.parse(item.filename).name;\n }\n const videoThumbnailMediaItem: DummyMediaItem = {\n type: 'dummy',\n id: item.id,\n filename,\n mimeType: null,\n srcURLs: {\n thumbnail: item.displayURLs.thumbnail\n }\n };\n const __config = {\n ...config,\n include: {\n ...config.include,\n allMediaVariants: true // Ensure variant name ('thumbnail') appears in dest filename\n }\n };\n tasks.push(\n ...await this.createFromDownloadable({\n config: __config,\n item: videoThumbnailMediaItem,\n destDir,\n fetcher,\n fileExistsAction,\n callbacks,\n limiter,\n signal,\n logger\n })\n );\n }\n\n if (tasks.length === 0) {\n throw Error('No src URL found');\n }\n\n return tasks;\n }\n\n static #pickVariant(urls: Record<string, any>, priority: string[]) {\n for (const variant of priority) {\n if (urls[variant]) {\n return {\n [variant]: urls[variant]\n };\n }\n }\n return {};\n }\n\n static findEmbedDownloader(edl: EmbedDownloader[], provider?: string | null) {\n return edl.find((dl) => dl.provider.toLowerCase() === provider?.trim().toLowerCase());\n }\n}\n"]}
1
+ {"version":3,"file":"DownloadTaskFactory.js","sourceRoot":"","sources":["../../../src/downloaders/task/DownloadTaskFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type DummyMediaItem } from '../../entities/MediaItem.js';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport MediaFilenameResolver from '../../utils/MediaFilenameResolver.js';\nimport {type DownloadTaskCallbacks} from './DownloadTask.js';\nimport DownloadTask from './DownloadTask.js';\nimport FetcherDownloadTask from './FetcherDownloadTask.js';\nimport { type Downloadable, isEmbed, isYouTubeEmbed } from '../../entities/Downloadable.js';\nimport { type EmbedDownloader, type FileExistsAction } from '../DownloaderOptions.js';\nimport type Logger from '../../utils/logging/Logger.js';\nimport YouTubeDownloadTask from './YouTubeDownloadTask.js';\nimport ExternalDownloaderTask from './ExternalDownloaderTask.js';\nimport { type DownloaderConfig } from '../Downloader.js';\nimport type Bottleneck from 'bottleneck';\n\nconst DEFAULT_IMAGE_URL_PRIORITY = [\n 'original',\n 'default',\n 'download',\n 'defaultSmall',\n 'thumbnailLarge',\n 'thumbnail',\n 'thumbnailSmall'\n];\n\nconst CAMPAIGN_COVER_PHOTO_URL_PRIORITY = [\n 'xlarge',\n 'large',\n 'medium',\n 'small',\n 'xsmall'\n];\n\nconst POST_COVER_IMAGE_URL_PRIORITY = [\n 'large',\n 'default',\n 'thumb',\n 'thumbSquareLarge',\n 'thumbSquare'\n];\n\nconst POST_THUMBNAIL_URL_PRIORITY = [\n 'large2',\n 'default',\n 'large',\n 'square'\n];\n\nconst NULL_VARIANT = '/*NULL*/';\n\nexport default class DownloadTaskFactory {\n\n static #getSrcURLs(item: Downloadable, allVariants: boolean) {\n if (item.type === 'image') {\n let urls: Record<string, string | null> = {};\n let priority: string[] = [];\n switch (item.imageType) {\n case 'single':\n return {\n [NULL_VARIANT]: item.imageURL\n };\n case 'default':\n urls = {\n ...item.imageURLs,\n download: item.downloadURL\n };\n priority = DEFAULT_IMAGE_URL_PRIORITY;\n break;\n case 'campaignCoverPhoto':\n urls = item.imageURLs;\n priority = CAMPAIGN_COVER_PHOTO_URL_PRIORITY;\n break;\n case 'postCoverImage':\n urls = item.imageURLs;\n priority = POST_COVER_IMAGE_URL_PRIORITY;\n break;\n case 'postThumbnail':\n urls = item.imageURLs;\n priority = POST_THUMBNAIL_URL_PRIORITY;\n break;\n }\n if (allVariants) {\n return urls;\n }\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'video') {\n let videoVariantName = 'display';\n if (item.size.width && item.size.height) {\n videoVariantName = `${item.size.width}x${item.size.height}`;\n }\n const urls = {\n download: item.downloadURL,\n [videoVariantName]: item.displayURLs.video\n };\n if (allVariants) {\n return urls;\n }\n const priority = [ 'download', videoVariantName ];\n return this.#pickVariant(urls, priority);\n }\n else if (item.type === 'audio') {\n return {\n [NULL_VARIANT]: item.url\n };\n }\n else if (item.type === 'file') {\n return {\n [NULL_VARIANT]: item.downloadURL\n };\n }\n else if (item.type === 'dummy') {\n return item.srcURLs;\n }\n else if (item.type === 'attachment') {\n return {\n [NULL_VARIANT]: item.downloadURL\n };\n }\n\n return {};\n }\n\n static async createFromDownloadable(params: {\n config: DownloaderConfig<any>,\n item: Downloadable,\n destDir: string,\n fetcher: Fetcher,\n fileExistsAction: FileExistsAction,\n callbacks?: DownloadTaskCallbacks | null,\n limiter: Bottleneck,\n signal?: AbortSignal,\n logger?: Logger | null;\n }) {\n\n const {\n config,\n item,\n destDir,\n fetcher,\n fileExistsAction,\n callbacks,\n limiter,\n signal,\n logger\n } = params;\n\n const embedDownloaders = config.embedDownloaders;\n const destFilenameFormat = config.filenameFormat.media;\n const downloadAllVariants = config.include.allMediaVariants;\n const tasks: DownloadTask[] = [];\n\n if (isEmbed(item)) {\n // Check if external downloader configured for embed item\n const embedDownloader = this.findEmbedDownloader(embedDownloaders, item.provider);\n if (embedDownloader) {\n const task = ExternalDownloaderTask.fromEmbedDownloader(config, embedDownloader, item, destDir, callbacks || null, logger);\n if (task) {\n tasks.push(task);\n }\n }\n // Use our own implementation if no external downloader configured for YT embeds\n else if (isYouTubeEmbed(item) && item.url) {\n tasks.push(await DownloadTask.create(YouTubeDownloadTask, {\n config,\n src: item.url,\n destDir,\n fileExistsAction,\n srcEntity: item,\n callbacks: callbacks || null,\n logger\n },\n limiter,\n signal));\n }\n }\n else {\n const srcURLs = this.#getSrcURLs(item, downloadAllVariants);\n for (const [ variant, url ] of Object.entries(srcURLs)) {\n const destFilenameResolver = \n new MediaFilenameResolver(item, url, destFilenameFormat,\n variant !== NULL_VARIANT ? variant : null, downloadAllVariants);\n\n if (url) {\n tasks.push(await DownloadTask.create(FetcherDownloadTask, {\n config,\n fetcher,\n src: url,\n destDir,\n destFilenameResolver,\n fileExistsAction,\n srcEntity: item,\n callbacks: callbacks || null,\n logger\n },\n limiter,\n signal));\n }\n }\n } \n \n // Create a DownloadTask backed by a DummyMediaItem for video thumbnail\n if (item.type === 'video' && item.displayURLs.thumbnail) {\n let filename: string | null = null;\n if (item.filename) {\n // Video file extension not applicable to thumbnail, so strip it.\n filename = path.parse(item.filename).name;\n }\n const videoThumbnailMediaItem: DummyMediaItem = {\n type: 'dummy',\n id: item.id,\n filename,\n mimeType: null,\n srcURLs: {\n thumbnail: item.displayURLs.thumbnail\n }\n };\n const __config = {\n ...config,\n include: {\n ...config.include,\n allMediaVariants: true // Ensure variant name ('thumbnail') appears in dest filename\n }\n };\n tasks.push(\n ...await this.createFromDownloadable({\n config: __config,\n item: videoThumbnailMediaItem,\n destDir,\n fetcher,\n fileExistsAction,\n callbacks,\n limiter,\n signal,\n logger\n })\n );\n }\n\n if (tasks.length === 0) {\n throw Error('No src URL found');\n }\n\n return tasks;\n }\n\n static #pickVariant(urls: Record<string, any>, priority: string[]) {\n for (const variant of priority) {\n if (urls[variant]) {\n return {\n [variant]: urls[variant]\n };\n }\n }\n return {};\n }\n\n static findEmbedDownloader(edl: EmbedDownloader[], provider?: string | null) {\n return edl.find((dl) => dl.provider.toLowerCase() === provider?.trim().toLowerCase());\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  export function isYouTubeEmbed(embed) {
2
- return embed.type === 'videoEmbed' && embed.provider === 'YouTube';
2
+ return embed.provider === 'YouTube';
3
3
  }
4
4
  export function isEmbed(data) {
5
5
  return data.type === 'videoEmbed' || data.type === 'linkEmbed' || data.type === 'unknownEmbed';
@@ -1 +1 @@
1
- {"version":3,"file":"Downloadable.js","sourceRoot":"","sources":["../../src/entities/Downloadable.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAkB;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC;AACjG,CAAC","sourcesContent":["import { type MediaItem } from './MediaItem.js';\nimport { type PostEmbed, type YouTubePostEmbed } from './Post.js';\n\nexport type Downloadable = MediaItem | PostEmbed;\n\nexport function isYouTubeEmbed(embed: PostEmbed): embed is YouTubePostEmbed {\n return embed.type === 'videoEmbed' && embed.provider === 'YouTube';\n}\n\nexport function isEmbed(data: Downloadable): data is PostEmbed {\n return data.type === 'videoEmbed' || data.type === 'linkEmbed' || data.type === 'unknownEmbed';\n}\n"]}
1
+ {"version":3,"file":"Downloadable.js","sourceRoot":"","sources":["../../src/entities/Downloadable.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,OAAO,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAkB;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC;AACjG,CAAC","sourcesContent":["import { type MediaItem } from './MediaItem.js';\nimport { type PostEmbed, type YouTubePostEmbed } from './Post.js';\n\nexport type Downloadable = MediaItem | PostEmbed;\n\nexport function isYouTubeEmbed(embed: PostEmbed): embed is YouTubePostEmbed {\n return embed.provider === 'YouTube';\n}\n\nexport function isEmbed(data: Downloadable): data is PostEmbed {\n return data.type === 'videoEmbed' || data.type === 'linkEmbed' || data.type === 'unknownEmbed';\n}\n"]}
@@ -89,7 +89,6 @@ export interface PostEmbed {
89
89
  url: string | null;
90
90
  }
91
91
  export type YouTubePostEmbed = PostEmbed & {
92
- type: 'videoEmbed';
93
92
  provider: 'YouTube';
94
93
  };
95
94
  //# sourceMappingURL=Post.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Post.d.ts","sourceRoot":"","sources":["../../src/entities/Post.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACzC,KAAK,EAAE,IAAI,EAAE,CAAC;IAEd;;;OAGG;IACH,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAExB;;;OAGG;IACH,WAAW,EAAE,YAAY,EAAE,CAAC;IAE5B;;;OAGG;IACH,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAElC;;;OAGG;IACH,MAAM,EAAE,YAAY,EAAE,CAAC;IAEvB;;;;OAIG;IACH,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAElC;;;;;OAKG;IACH,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAE3B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,GAAG,MAAM,QAAQ,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"Post.d.ts","sourceRoot":"","sources":["../../src/entities/Post.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACzC,KAAK,EAAE,IAAI,EAAE,CAAC;IAEd;;;OAGG;IACH,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAExB;;;OAGG;IACH,WAAW,EAAE,YAAY,EAAE,CAAC;IAE5B;;;OAGG;IACH,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAE3B;;;OAGG;IACH,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAElC;;;OAGG;IACH,MAAM,EAAE,YAAY,EAAE,CAAC;IAEvB;;;;OAIG;IACH,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAElC;;;;;OAKG;IACH,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAE3B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAE1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,GAAG,MAAM,QAAQ,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Post.js","sourceRoot":"","sources":["../../src/entities/Post.ts"],"names":[],"mappings":"","sourcesContent":["import { type Campaign } from './Campaign.js';\nimport { type Downloadable } from './Downloadable.js';\nimport { type PostCoverImageMediaItem, type PostThumbnailMediaItem } from './MediaItem.js';\nimport { type Tier } from './Reward.js';\n\nexport interface PostCollection {\n url: string;\n posts: Post[];\n total: number | null;\n nextURL: string | null;\n}\n\nexport interface Post {\n type: 'post';\n id: string;\n /**\n * @privateRemarks\n *\n * `data.attibutes.post_type`\n *\n * Known types:\n * - image_file\n * - video_external_file\n * - audio_file\n * - text_only\n * - poll\n * - link: only embedded link info is saved. Link is not followed / downloaded.\n * - video_embed - only embedded video info is saved. Video itself is not downloaded.\n */\n postType: string;\n isViewable: boolean;\n url: string | null;\n title: string | null;\n content: string | null;\n teaserText: string | null;\n publishedAt: string | null;\n editedAt: string | null;\n coverImage: PostCoverImageMediaItem | null;\n thumbnail: PostThumbnailMediaItem | null;\n tiers: Tier[];\n\n /**\n * @privateRemarks\n * `data.attibutes.embed`\n */\n embed: PostEmbed | null;\n\n /**\n * @privateRemarks\n * `data.relationships.attachments`\n */\n attachments: Downloadable[];\n\n /**\n * @privateRemarks\n * `data.relationships.audio`\n */\n audio: Downloadable | null;\n\n /**\n * @privateRemarks\n * `data.relationships.audio_prevew`\n */\n audioPreview: Downloadable | null;\n\n /**\n * @privateRemarks\n * `data.relationships.images`\n */\n images: Downloadable[];\n\n /**\n * @privateRemarks\n * Not included in `data.relationships`\n * Converted from `data.attributes.video_preview`\n */\n videoPreview: Downloadable | null;\n\n /**\n * @privateRemarks\n * Not included in `data.relationships`\n * Converted from `data.attributes.post_file` with\n * `data.attributes.post_type` matching 'video_external_file'\n */\n video: Downloadable | null;\n\n campaign: Campaign | null;\n\n raw: object;\n}\n\nexport interface PostEmbed {\n id: `${string}-embed`;\n postId: string;\n type: 'videoEmbed' | 'linkEmbed' | 'unknownEmbed';\n description: string | null;\n html: string | null;\n provider: string | null;\n providerURL: string | null;\n subject: string | null;\n url: string | null;\n}\n\nexport type YouTubePostEmbed = PostEmbed & { type: 'videoEmbed'; provider: 'YouTube' }\n"]}
1
+ {"version":3,"file":"Post.js","sourceRoot":"","sources":["../../src/entities/Post.ts"],"names":[],"mappings":"","sourcesContent":["import { type Campaign } from './Campaign.js';\nimport { type Downloadable } from './Downloadable.js';\nimport { type PostCoverImageMediaItem, type PostThumbnailMediaItem } from './MediaItem.js';\nimport { type Tier } from './Reward.js';\n\nexport interface PostCollection {\n url: string;\n posts: Post[];\n total: number | null;\n nextURL: string | null;\n}\n\nexport interface Post {\n type: 'post';\n id: string;\n /**\n * @privateRemarks\n *\n * `data.attibutes.post_type`\n *\n * Known types:\n * - image_file\n * - video_external_file\n * - audio_file\n * - text_only\n * - poll\n * - link: only embedded link info is saved. Link is not followed / downloaded.\n * - video_embed - only embedded video info is saved. Video itself is not downloaded.\n */\n postType: string;\n isViewable: boolean;\n url: string | null;\n title: string | null;\n content: string | null;\n teaserText: string | null;\n publishedAt: string | null;\n editedAt: string | null;\n coverImage: PostCoverImageMediaItem | null;\n thumbnail: PostThumbnailMediaItem | null;\n tiers: Tier[];\n\n /**\n * @privateRemarks\n * `data.attibutes.embed`\n */\n embed: PostEmbed | null;\n\n /**\n * @privateRemarks\n * `data.relationships.attachments`\n */\n attachments: Downloadable[];\n\n /**\n * @privateRemarks\n * `data.relationships.audio`\n */\n audio: Downloadable | null;\n\n /**\n * @privateRemarks\n * `data.relationships.audio_prevew`\n */\n audioPreview: Downloadable | null;\n\n /**\n * @privateRemarks\n * `data.relationships.images`\n */\n images: Downloadable[];\n\n /**\n * @privateRemarks\n * Not included in `data.relationships`\n * Converted from `data.attributes.video_preview`\n */\n videoPreview: Downloadable | null;\n\n /**\n * @privateRemarks\n * Not included in `data.relationships`\n * Converted from `data.attributes.post_file` with\n * `data.attributes.post_type` matching 'video_external_file'\n */\n video: Downloadable | null;\n\n campaign: Campaign | null;\n\n raw: object;\n}\n\nexport interface PostEmbed {\n id: `${string}-embed`;\n postId: string;\n type: 'videoEmbed' | 'linkEmbed' | 'unknownEmbed';\n description: string | null;\n html: string | null;\n provider: string | null;\n providerURL: string | null;\n subject: string | null;\n url: string | null;\n}\n\nexport type YouTubePostEmbed = PostEmbed & { provider: 'YouTube' }\n"]}
@@ -10,7 +10,7 @@ export default abstract class FilenameResolver<T> {
10
10
  protected srcURL: string;
11
11
  constructor(target: T, srcURL: string);
12
12
  abstract resolve(response: Response): string;
13
- protected getFilenamePartsFromResponse(response: Response): {
13
+ protected getFilenamePartsFromResponse(response: Response, extByContentType?: boolean): {
14
14
  name: string;
15
15
  ext: string;
16
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"FllenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAKA;;;;;;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"}
1
+ {"version":3,"file":"FllenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/FllenameResolver.ts"],"names":[],"mappings":"AAKA;;;;;;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,EAAE,gBAAgB,UAAO;;;;IA+BlF,SAAS,CAAC,yBAAyB,CAAC,IAAI,EAAE,MAAM;CAuBjD"}
@@ -14,7 +14,7 @@ export default class FilenameResolver {
14
14
  this.target = target;
15
15
  this.srcURL = srcURL;
16
16
  }
17
- getFilenamePartsFromResponse(response) {
17
+ getFilenamePartsFromResponse(response, extByContentType = true) {
18
18
  const parts = {
19
19
  name: '',
20
20
  ext: ''
@@ -30,12 +30,14 @@ export default class FilenameResolver {
30
30
  }
31
31
  }
32
32
  // Filename obtained from content-disposition could have wrong extension.
33
- // Always use extension derived from headers content-type if available.
34
- const contentType = response.headers.get('content-type') || null;
35
- if (contentType) {
36
- const extByContentType = this.getExtensionByContentType(contentType);
37
- if (extByContentType) {
38
- parts.ext = extByContentType;
33
+ // The default behavior is to obtain extension from content-type header if available.
34
+ if (extByContentType) {
35
+ const contentType = response.headers.get('content-type') || null;
36
+ if (contentType) {
37
+ const _extByContentType = this.getExtensionByContentType(contentType);
38
+ if (_extByContentType) {
39
+ parts.ext = _extByContentType;
40
+ }
39
41
  }
40
42
  }
41
43
  return parts;
@@ -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;AACnC,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,CAAC;YAChB,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,CAAC;gBACb,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;YACzB,CAAC;QACH,CAAC;QACD,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;QACjE,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,gBAAgB,GAAG,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC;YACrE,IAAI,gBAAgB,EAAE,CAAC;gBACrB,KAAK,CAAC,GAAG,GAAG,gBAAgB,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAES,yBAAyB,CAAC,IAAY;QAC9C,MAAM,cAAc,GAAG;YACrB,0BAA0B;YAC1B,YAAY;SACb,CAAC;QACF,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,OAAO,EAAE,CAAC;QACZ,CAAC;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,CAAC;YAC/D,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,+DAA+D;aAC1D,IAAI,IAAI,KAAK,YAAY,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YACxD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,GAAG,GAAG,KAAK,CAAC;QACd,CAAC;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 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.find((v) => type.includes(v))) {\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"]}
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;AACnC,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,EAAE,gBAAgB,GAAG,IAAI;QAChF,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,CAAC;YAChB,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,CAAC;gBACb,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;YACzB,CAAC;QACH,CAAC;QACD,yEAAyE;QACzE,qFAAqF;QACrF,IAAI,gBAAgB,EAAE,CAAC;YACrB,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;YACjE,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,iBAAiB,GAAG,IAAI,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC;gBACtE,IAAI,iBAAiB,EAAE,CAAC;oBACtB,KAAK,CAAC,GAAG,GAAG,iBAAiB,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAES,yBAAyB,CAAC,IAAY;QAC9C,MAAM,cAAc,GAAG;YACrB,0BAA0B;YAC1B,YAAY;SACb,CAAC;QACF,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,OAAO,EAAE,CAAC;QACZ,CAAC;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,CAAC;YAC/D,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,+DAA+D;aAC1D,IAAI,IAAI,KAAK,YAAY,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YACxD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,GAAG,GAAG,KAAK,CAAC;QACd,CAAC;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 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, extByContentType = true) {\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 // The default behavior is to obtain extension from content-type header if available.\n if (extByContentType) {\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\n return parts;\n }\n\n protected getExtensionByContentType(type: string) {\n const undeterminable = [\n 'application/octet-stream',\n 'text/plain'\n ];\n if (undeterminable.find((v) => type.includes(v))) {\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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"MediaFilenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/MediaFilenameResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,gBAAgB,MAAM,uBAAuB,CAAC;AAIrD,MAAM,CAAC,OAAO,OAAO,qBAAqB,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;;gBAK7E,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,aAAa,EAAE,OAAO;IAUrG,OAAO,CAAC,QAAQ,EAAE,QAAQ;CA8D3B"}
1
+ {"version":3,"file":"MediaFilenameResolver.d.ts","sourceRoot":"","sources":["../../src/utils/MediaFilenameResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,gBAAgB,MAAM,uBAAuB,CAAC;AAIrD,MAAM,CAAC,OAAO,OAAO,qBAAqB,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;;gBAK7E,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,aAAa,EAAE,OAAO;IAUrG,OAAO,CAAC,QAAQ,EAAE,QAAQ;CAiE3B"}
@@ -28,7 +28,7 @@ class MediaFilenameResolver extends FilenameResolver {
28
28
  resolve(response) {
29
29
  const mi = this.target;
30
30
  const miFilenameParts = mi.filename ? path.parse(mi.filename) : null;
31
- const resFilenameParts = this.getFilenamePartsFromResponse(response);
31
+ const resFilenameParts = this.getFilenamePartsFromResponse(response, mi.type !== 'attachment');
32
32
  const filenameParts = {
33
33
  name: '',
34
34
  ext: ''
@@ -50,12 +50,15 @@ class MediaFilenameResolver extends FilenameResolver {
50
50
  }
51
51
  /**
52
52
  * Obtain `extension` from (in order of priority):
53
- * 1. Response headers
53
+ * 1. Response headers (unless target is attachment, in which case (2) takes precedence)
54
54
  * 2. Media filename
55
55
  * 3. Media mimetype
56
56
  * 4. Src URL
57
57
  */
58
- if (resFilenameParts.ext) {
58
+ if (mi.type === 'attachment' && miFilenameParts?.ext) {
59
+ filenameParts.ext = miFilenameParts.ext;
60
+ }
61
+ else if (resFilenameParts.ext) {
59
62
  // Use media item filename if the same (preserves case)
60
63
  if (miFilenameParts?.ext?.toLowerCase() === resFilenameParts.ext.toLowerCase() &&
61
64
  filenameParts.name === miFilenameParts.name && __classPrivateFieldGet(this, _MediaFilenameResolver_format, "f").includes('{media.filename}')) {
@@ -1 +1 @@
1
- {"version":3,"file":"MediaFilenameResolver.js","sourceRoot":"","sources":["../../src/utils/MediaFilenameResolver.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type MediaLike } from '../entities/MediaItem.js';\nimport FilenameResolver from './FllenameResolver.js';\nimport URLHelper from './URLHelper.js';\nimport FilenameFormatHelper from './FilenameFormatHelper.js';\n\nexport default class MediaFilenameResolver<T extends MediaLike> extends FilenameResolver<T> {\n\n #format: string;\n #variant: string | null;\n\n constructor(target: T, srcURL: string, format: string, variant: string | null, ensureVariant: boolean) {\n super(target, srcURL);\n this.#format = format;\n this.#variant = variant;\n\n if (ensureVariant && !this.#format.includes('{media.variant}')) {\n this.#format = `${format}[-]?{media.variant}`;\n }\n }\n\n resolve(response: Response) {\n const mi = this.target;\n const miFilenameParts = mi.filename ? path.parse(mi.filename) : null;\n const resFilenameParts = this.getFilenamePartsFromResponse(response);\n\n const filenameParts = {\n name: '',\n ext: ''\n };\n\n /**\n * Obtain `name` part from (in order of priority):\n * 1. Media filename\n * 2. Filename from response headers\n * 3. If none of the above is available, fallback to using the media ID\n */\n if (miFilenameParts?.name) {\n filenameParts.name = miFilenameParts.name;\n }\n else if (resFilenameParts.name) {\n filenameParts.name = resFilenameParts.name;\n }\n else if (!this.#format.includes('{media.id}')) {\n filenameParts.name = mi.id;\n }\n\n /**\n * Obtain `extension` from (in order of priority):\n * 1. Response headers\n * 2. Media filename\n * 3. Media mimetype\n * 4. Src URL\n */\n if (resFilenameParts.ext) {\n // Use media item filename if the same (preserves case)\n if (miFilenameParts?.ext?.toLowerCase() === resFilenameParts.ext.toLowerCase() &&\n filenameParts.name === miFilenameParts.name && this.#format.includes('{media.filename}')) {\n\n filenameParts.ext = miFilenameParts.ext;\n }\n else {\n filenameParts.ext = resFilenameParts.ext;\n }\n }\n else if (miFilenameParts?.ext) {\n filenameParts.ext = miFilenameParts.ext;\n }\n else if (mi.mimeType) {\n filenameParts.ext = this.getExtensionByContentType(mi.mimeType);\n }\n else {\n filenameParts.ext = URLHelper.getExtensionFromURL(this.srcURL);\n }\n\n const tmpMI = {\n ...mi,\n filename: filenameParts.name,\n variant: this.#variant\n };\n\n return FilenameFormatHelper.getMediaFilename(tmpMI, this.#format, filenameParts.ext);\n }\n}\n"]}
1
+ {"version":3,"file":"MediaFilenameResolver.js","sourceRoot":"","sources":["../../src/utils/MediaFilenameResolver.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { type MediaLike } from '../entities/MediaItem.js';\nimport FilenameResolver from './FllenameResolver.js';\nimport URLHelper from './URLHelper.js';\nimport FilenameFormatHelper from './FilenameFormatHelper.js';\n\nexport default class MediaFilenameResolver<T extends MediaLike> extends FilenameResolver<T> {\n\n #format: string;\n #variant: string | null;\n\n constructor(target: T, srcURL: string, format: string, variant: string | null, ensureVariant: boolean) {\n super(target, srcURL);\n this.#format = format;\n this.#variant = variant;\n\n if (ensureVariant && !this.#format.includes('{media.variant}')) {\n this.#format = `${format}[-]?{media.variant}`;\n }\n }\n\n resolve(response: Response) {\n const mi = this.target;\n const miFilenameParts = mi.filename ? path.parse(mi.filename) : null;\n const resFilenameParts = this.getFilenamePartsFromResponse(response, mi.type !== 'attachment');\n\n const filenameParts = {\n name: '',\n ext: ''\n };\n\n /**\n * Obtain `name` part from (in order of priority):\n * 1. Media filename\n * 2. Filename from response headers\n * 3. If none of the above is available, fallback to using the media ID\n */\n if (miFilenameParts?.name) {\n filenameParts.name = miFilenameParts.name;\n }\n else if (resFilenameParts.name) {\n filenameParts.name = resFilenameParts.name;\n }\n else if (!this.#format.includes('{media.id}')) {\n filenameParts.name = mi.id;\n }\n\n /**\n * Obtain `extension` from (in order of priority):\n * 1. Response headers (unless target is attachment, in which case (2) takes precedence)\n * 2. Media filename\n * 3. Media mimetype\n * 4. Src URL\n */\n if (mi.type === 'attachment' && miFilenameParts?.ext) {\n filenameParts.ext = miFilenameParts.ext;\n }\n else if (resFilenameParts.ext) {\n // Use media item filename if the same (preserves case)\n if (miFilenameParts?.ext?.toLowerCase() === resFilenameParts.ext.toLowerCase() &&\n filenameParts.name === miFilenameParts.name && this.#format.includes('{media.filename}')) {\n\n filenameParts.ext = miFilenameParts.ext;\n }\n else {\n filenameParts.ext = resFilenameParts.ext;\n }\n }\n else if (miFilenameParts?.ext) {\n filenameParts.ext = miFilenameParts.ext;\n }\n else if (mi.mimeType) {\n filenameParts.ext = this.getExtensionByContentType(mi.mimeType);\n }\n else {\n filenameParts.ext = URLHelper.getExtensionFromURL(this.srcURL);\n }\n\n const tmpMI = {\n ...mi,\n filename: filenameParts.name,\n variant: this.#variant\n };\n\n return FilenameFormatHelper.getMediaFilename(tmpMI, this.#format, filenameParts.ext);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patreon-dl",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Patreon Downloader",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",
@@ -84,7 +84,7 @@
84
84
  "speedometer": "^1.1.0",
85
85
  "string-argv": "^0.3.2",
86
86
  "yargs-parser": "^21.1.1",
87
- "youtubei.js": "^12.1.0"
87
+ "youtubei.js": "^12.2.0"
88
88
  },
89
89
  "keywords": [
90
90
  "patreon",