patreon-dl 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +29 -1
  2. package/dist/browse/api/CampaignAPIMixin.d.ts +1 -1
  3. package/dist/browse/api/ContentAPIMixin.d.ts +3 -3
  4. package/dist/browse/api/FilterAPIMixin.d.ts +1 -1
  5. package/dist/browse/api/index.d.ts +10 -10
  6. package/dist/browse/db/CampaignDBMixin.d.ts +3 -3
  7. package/dist/browse/db/ContentDBMixin.d.ts +13 -13
  8. package/dist/browse/db/index.d.ts +16 -16
  9. package/dist/browse/web/assets/index-CQnQD2xz.css +1 -0
  10. package/dist/browse/web/assets/index-CtZbx-Du.js +218 -0
  11. package/dist/browse/web/index.html +2 -2
  12. package/dist/cli/CLIOptions.js +2 -0
  13. package/dist/cli/CLIOptions.js.map +1 -1
  14. package/dist/cli/CommandLineParser.d.ts +4 -0
  15. package/dist/cli/CommandLineParser.js +65 -32
  16. package/dist/cli/CommandLineParser.js.map +1 -1
  17. package/dist/cli/ConfigFileParser.js +2 -0
  18. package/dist/cli/ConfigFileParser.js.map +1 -1
  19. package/dist/cli/helper/PostList.d.ts +6 -0
  20. package/dist/cli/helper/PostList.js +106 -0
  21. package/dist/cli/helper/PostList.js.map +1 -0
  22. package/dist/cli/index.js +73 -53
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/downloaders/Bootstrap.d.ts +5 -0
  25. package/dist/downloaders/Bootstrap.js +10 -0
  26. package/dist/downloaders/Bootstrap.js.map +1 -1
  27. package/dist/downloaders/Downloader.d.ts +4 -4
  28. package/dist/downloaders/Downloader.js +5 -58
  29. package/dist/downloaders/Downloader.js.map +1 -1
  30. package/dist/downloaders/DownloaderOptions.d.ts +1 -0
  31. package/dist/downloaders/DownloaderOptions.js +2 -0
  32. package/dist/downloaders/DownloaderOptions.js.map +1 -1
  33. package/dist/downloaders/IncludeCriteriaHelper.d.ts +25 -0
  34. package/dist/downloaders/IncludeCriteriaHelper.js +160 -0
  35. package/dist/downloaders/IncludeCriteriaHelper.js.map +1 -0
  36. package/dist/downloaders/InitialData.js +20 -6
  37. package/dist/downloaders/InitialData.js.map +1 -1
  38. package/dist/downloaders/PostDownloader.js +62 -98
  39. package/dist/downloaders/PostDownloader.js.map +1 -1
  40. package/dist/downloaders/PostsFetcher.js +4 -2
  41. package/dist/downloaders/PostsFetcher.js.map +1 -1
  42. package/dist/downloaders/ProductDownloader.js +31 -21
  43. package/dist/downloaders/ProductDownloader.js.map +1 -1
  44. package/dist/downloaders/task/M3U8DownloadTask.d.ts +1 -0
  45. package/dist/downloaders/task/M3U8DownloadTask.js +137 -39
  46. package/dist/downloaders/task/M3U8DownloadTask.js.map +1 -1
  47. package/dist/downloaders/task/YouTubeDownloadTask.js +1 -3
  48. package/dist/downloaders/task/YouTubeDownloadTask.js.map +1 -1
  49. package/dist/parsers/PageParser.js +8 -3
  50. package/dist/parsers/PageParser.js.map +1 -1
  51. package/dist/utils/Fetcher.d.ts +4 -1
  52. package/dist/utils/Fetcher.js +6 -1
  53. package/dist/utils/Fetcher.js.map +1 -1
  54. package/dist/utils/URLHelper.d.ts +4 -0
  55. package/dist/utils/URLHelper.js +17 -0
  56. package/dist/utils/URLHelper.js.map +1 -1
  57. package/dist/utils/YouTubeCredentialsCapturer.js +1 -1
  58. package/dist/utils/YouTubeCredentialsCapturer.js.map +1 -1
  59. package/dist/utils/logging/ConsoleLogger.js +5 -0
  60. package/dist/utils/logging/ConsoleLogger.js.map +1 -1
  61. package/dist/utils/yt/InnertubeLoader.d.ts +4 -6
  62. package/dist/utils/yt/InnertubeLoader.js +14 -14
  63. package/dist/utils/yt/InnertubeLoader.js.map +1 -1
  64. package/dist/utils/yt/PoToken.js +53 -27
  65. package/dist/utils/yt/PoToken.js.map +1 -1
  66. package/package.json +12 -8
  67. package/dist/browse/web/assets/index-CW4CUoWl.css +0 -1
  68. package/dist/browse/web/assets/index-CziUWlSw.js +0 -218
@@ -1 +1 @@
1
- {"version":3,"file":"ProductDownloader.js","sourceRoot":"","sources":["../../src/downloaders/ProductDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import ProductParser from '../parsers/ProductParser.js';\nimport URLHelper from '../utils/URLHelper.js';\nimport Downloader, { type DownloaderConfig, type DownloaderStartParams } from './Downloader.js';\nimport StatusCache from './cache/StatusCache.js';\nimport { generateProductSummary } from './templates/ProductInfo.js';\nimport path from 'path';\nimport { TargetSkipReason } from './DownloaderEvent.js';\nimport { ProductType, type Product } from '../entities/Product.js';\nimport { type Downloadable } from '../entities/Downloadable.js';\nimport Bootstrap from './Bootstrap.js';\nimport ProductsFetcher from './ProductsFetcher.js';\nimport { type ProductDirectories } from '../utils/FSHelper.js';\nimport { type DBInstance } from '../browse/db/index.js';\n\nexport default class ProductDownloader extends Downloader<Product> {\n\n static version = '1.0.1';\n\n name = 'ProductDownloader';\n\n #startPromise: Promise<void> | null = null;\n\n doStart(params?: DownloaderStartParams): Promise<void> {\n\n if (this.#startPromise) {\n throw Error('Downloader already running');\n }\n\n this.#startPromise = this.#doStart(params)\n .finally(() => {\n this.#startPromise = null;\n });\n\n return this.#startPromise;\n }\n\n async #doStart(params?: DownloaderStartParams): Promise<void> {\n try {\n const { signal } = params || {};\n const productFetch = this.config.productFetch;\n const db = await this.db();\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n\n if (productFetch.type === 'byShop') {\n this.log('info', `Targeting products by '${productFetch.vanity}'`);\n }\n else if (productFetch.type === 'byFile') {\n this.log('info', `Target product given by API data in \"${productFetch.filePath}\"`);\n }\n else {\n this.log('info', `Targeting product #${productFetch.productId}`);\n }\n\n // Step 1: get products (if by shop) or target product\n const productsFetcher = new ProductsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n productsFetcher.on('statusChange', ({current}) => {\n if (current.status === 'running') {\n this.emit('fetchBegin', { targetType: productsFetcher.getTargetType() });\n }\n });\n productsFetcher.begin();\n\n // Step 2: download products in each fetched list\n let downloaded = 0;\n let skippedUnviewable = 0;\n let skippedRedundant = 0;\n let skippedPublishDateOutOfRange = 0;\n let campaignSaved = false;\n let stopConditionMet = false;\n const productsParser = new ProductParser(this.logger);\n while (productsFetcher.hasNext()) {\n const { list, aborted, error } = await productsFetcher.next();\n if (!list || aborted) {\n break;\n }\n if (!list && error) {\n this.emit('end', { aborted: false, error, message: 'ProductsFetcher error' });\n return;\n }\n if (!campaignSaved && list.items[0]?.campaign) {\n await this.saveCampaignInfo(list.items[0].campaign, signal);\n campaignSaved = true;\n if (this.checkAbortSignal(signal)) {\n return;\n }\n }\n\n for (const _product of list.items) {\n\n if (stopConditionMet) {\n break;\n }\n\n let product = _product;\n\n if (this.#isFetchingMultipleProducts(productFetch)) {\n // Refresh to ensure media links have not expired\n this.log('debug', `Refresh product #${_product.id}`);\n const productURL = URLHelper.constructProductAPIURL(product.id);\n const { json } = await this.commonFetchAPI(productURL, signal);\n let refreshed: Product | null = null;\n if (json) {\n refreshed = productsParser.parseProductsAPIResponse(json, productURL).items[0] || null;\n if (!refreshed) {\n this.log('warn', `Refreshed product #${_product.id} but got empty value - going to use existing data`);\n }\n else if (refreshed.id !== _product.id) {\n this.log('warn', `Refreshed product #${_product.id} but ID mismatch - going to use existing data`);\n refreshed = null;\n }\n else {\n // Refreshed data does not have productType - reinstate it\n refreshed.productType = _product.productType;\n }\n }\n else if (this.checkAbortSignal(signal)) {\n return;\n }\n else {\n this.log('warn', `Failed to refresh product #${_product.id} - going to use existing data`);\n }\n if (refreshed) {\n this.log('debug', `Use refreshed product data #${refreshed.id}`);\n product = refreshed;\n }\n }\n\n this.emit('targetBegin', { target: product });\n\n // Step 4.1: product directories\n const productDirs = this.fsHelper.getProductDirs(product);\n this.log('debug', 'Product directories:', productDirs);\n\n // Step 4.2: Check with status cache\n const statusCache = StatusCache.getInstance(this.config, productDirs.statusCache, this.logger);\n if (!product.productType || product.productType === ProductType.DigitalCommerce) {\n const statusCacheValidation = statusCache.validate(product, productDirs.root, this.config)\n if (!statusCacheValidation.invalidated) {\n this.log('info', `Skipped downloading product #${product.id}: already downloaded`);\n this.emit('targetEnd', {\n target: product,\n isSkipped: true,\n skipReason: TargetSkipReason.AlreadyDownloaded,\n skipMessage: 'Target already downloaded'\n });\n skippedRedundant++;\n if (this.config.stopOn === 'previouslyDownloaded') {\n stopConditionMet = true;\n }\n continue;\n }\n }\n \n switch ((await this.#doDownload(\n product,\n productDirs,\n statusCache,\n db,\n signal\n )).status) {\n case 'aborted':\n return;\n case 'downloaded':\n downloaded++;\n break;\n case 'skippedPublishDateOutOfRange':\n skippedPublishDateOutOfRange++;\n if (this.config.stopOn === 'postPublishDateOutOfRange' ||\n this.config.stopOn === 'publishDateOutOfRange'\n ) {\n stopConditionMet = true;\n }\n break;\n case 'skippedUnviewable':\n skippedUnviewable++;\n break;\n }\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n }\n\n if (stopConditionMet) {\n break;\n }\n }\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n\n if (stopConditionMet && this.#isFetchingMultipleProducts(productFetch)) {\n this.log('info', `Stop downloader: stop condition \"${this.config.stopOn}\" met`)\n }\n\n // Done\n if (productFetch.type === 'byShop') {\n this.log('info', `Done downloading products by '${productFetch.vanity}'`);\n }\n else if (productFetch.type === 'byFile') {\n this.log('info', `Done downloading product given in \"${productFetch.filePath}\"`);\n }\n else {\n this.log('info', `Done downloading product #${productFetch.productId}`);\n }\n let endMessage = 'Done';\n if (this.#isFetchingMultipleProducts(productFetch)) {\n const skippedStrParts: string[] = [];\n if (skippedUnviewable) {\n skippedStrParts.push(`${skippedUnviewable} unviewable`);\n }\n if (skippedRedundant) {\n skippedStrParts.push(`${skippedRedundant} redundant`);\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} / ${productsFetcher.getTotal()} products processed${skippedStr}`;\n this.log('info', endMessage);\n }\n this.emit('end', { aborted: false, message: endMessage });\n }\n finally {\n await this.closeDB();\n if (this.logger) {\n await this.logger.end();\n }\n }\n }\n\n async #doDownload(\n product: Product,\n productDirs: ProductDirectories,\n statusCache: StatusCache,\n db: DBInstance,\n signal?: AbortSignal\n ): Promise<{status: 'skippedUnviewable' | 'skippedPublishDateOutOfRange' | 'aborted' | 'downloaded' }> {\n // Step 1: Check whether we should download product\n // -- 1.1 Accessibility\n if (!product.isAccessible) {\n if (this.config.include.lockedContent) {\n this.log('warn', `Product #${product.id} is not accessible by current user`);\n }\n else {\n this.log('warn', `Skipped downloading product #${product.id}: not accessible by current user`);\n this.emit('targetEnd', {\n target: product,\n isSkipped: true,\n skipReason: TargetSkipReason.Inaccessible,\n skipMessage: 'Target is not accessible by current user'\n });\n return {\n status: 'skippedUnviewable'\n };\n }\n }\n // -- 1.4 Config option 'include.productsPublished'\n if (this.isPublishDateOutOfRange(product)) {\n return {\n status: 'skippedPublishDateOutOfRange'\n };\n }\n\n this.fsHelper.createDir(productDirs.root);\n\n // Step 2: save product info\n if (this.config.include.contentInfo) {\n this.log('info', 'Save product info');\n this.emit('phaseBegin', { target: product, phase: 'saveInfo' });\n this.fsHelper.createDir(productDirs.info);\n const summary = generateProductSummary(product);\n const summaryFile = path.resolve(productDirs.info, 'info.txt');\n const saveSummaryResult = await this.fsHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);\n this.logWriteTextFileResult(saveSummaryResult, product, 'product summary');\n\n const productRawFile = path.resolve(productDirs.info, 'product-api.json');\n const saveProductRawResult = await this.fsHelper.writeTextFile(\n productRawFile, product.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(saveProductRawResult, product, 'product API data');\n this.emit('phaseEnd', { target: product, phase: 'saveInfo' });\n }\n\n // Step 3: Download product media items\n const incPreview = this.config.include.previewMedia;\n const incContent = this.config.include.contentMedia;\n this.log('info', 'Download:', {\n ['preview items']: incPreview === true ? 'yes' : incPreview === false ? 'no' : JSON.stringify(incPreview),\n ['content items']: incContent === true ? 'yes' : incContent === false ? 'no' : JSON.stringify(incContent)\n });\n\n const __inc = (inc: boolean | Array<any>, target: Downloadable) => {\n if (typeof inc === 'boolean') {\n return inc;\n }\n return (inc.includes(target.type as any));\n };\n\n const previewMedia = product.previewMedia.filter((tt) => __inc(incPreview, tt));\n const contentMedia = product.contentMedia.filter((tt) => __inc(incContent, tt));\n const campaignDir = product.campaign ? this.fsHelper.getCampaignDirs(product.campaign).root : null;\n\n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseBegin', { target: product, phase: 'saveMedia' });\n const batchResult = await this.createDownloadTaskBatch(\n `Product #${product.id} (${product.name})`,\n signal,\n\n previewMedia.length > 0 ? {\n target: previewMedia,\n targetName: `product #${product.id} -> preview media`,\n src: product,\n dirs: {\n campaign: campaignDir,\n main: productDirs.previewMedia,\n thumbnails: productDirs.thumbnails\n },\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n contentMedia.length > 0 ? {\n target: contentMedia,\n targetName: `product #${product.id} -> content media`,\n src: product,\n dirs: {\n campaign: campaignDir,\n main: productDirs.contentMedia,\n thumbnails: productDirs.thumbnails\n },\n fileExistsAction: this.config.fileExistsAction.content,\n ignoreCreateTaskErrors: !product.isAccessible\n } : null\n );\n\n const { batch } = batchResult;\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 try {\n if (this.checkAbortSignal(signal)) {\n return {\n status: 'aborted'\n };\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: product, phase: 'batchDownload', batch });\n\n await batch.start();\n\n // Step 4: Update status cache\n statusCache.updateOnDownload(product, productDirs.root, batch.getTasks('error').length > 0 || batchResult.errorCount > 0, this.config);\n\n await batch.destroy();\n }\n finally {\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n }\n\n this.emit('phaseEnd', { target: product, phase: 'batchDownload' });\n this.emit('phaseEnd', { target: product, phase: 'saveMedia' });\n }\n\n if (this.checkAbortSignal(signal)) {\n return {\n status: 'aborted'\n };\n }\n\n // Step 5: Save to DB\n let skipDB = false;\n let saveReferencedEntityId: (id: string) => void;\n const dbProduct = db.getContent(product.id, 'product');\n if (!product.isAccessible) {\n // Skip if existing db record (if any) refers to accessible product\n skipDB = dbProduct !== null && dbProduct.isAccessible;\n }\n if (!skipDB) {\n db.saveContent(product);\n saveReferencedEntityId = (id) => {\n product.referencedEntityId = id;\n db.saveContent(product);\n };\n }\n else {\n this.log('info', `Skip overwrite existing accessible product #${product.id} in DB with current unviewable version`);\n saveReferencedEntityId = (id) => {\n // Ensure referencedEntityId is updated\n dbProduct!.referencedEntityId = id;\n db.saveContent(dbProduct!);\n };\n }\n\n // Step 6: check product type and download referenced product / collection\n await this.#processProductType(product, saveReferencedEntityId, signal);\n\n // Done\n this.log('info', `Done downloading product #${product.id}`);\n this.emit('targetEnd', { target: product, isSkipped: false });\n\n return {\n status: 'downloaded'\n }\n }\n\n // Carry out further action based on product type\n async #processProductType(\n product: Product,\n saveReferencedEntityId: (id: string) => void,\n signal?: AbortSignal\n ) {\n if (product.productType === undefined || product.productType === ProductType.DigitalCommerce) {\n this.log('debug', `Product type \"${product.productType}\": no further action required`);\n return;\n }\n if (product.productType === ProductType.Post || product.productType === ProductType.Collection) {\n const bootstrap = Bootstrap.getDownloaderBootstrapDataByURL(product.url);\n if (bootstrap?.type === 'post') {\n this.log('info', `Download ${product.productType} referenced by product: ${product.url}`);\n const PostDownloader = (await import('./PostDownloader.js')).default;\n const downloader = new PostDownloader(\n {\n ...this.config,\n ...bootstrap\n },\n this.db,\n this.logger,\n {\n skipSaveCampaign: true,\n keepLoggerOpen: true,\n keepDBOpen: true\n }\n );\n try {\n await downloader.start({ signal });\n this.log('info', `Done downloading ${product.productType} referenced by product`);\n }\n catch (error) {\n if (signal?.aborted) {\n throw error;\n }\n this.log('error', `Error downloading ${product.productType} referenced by product (url: ${product.url})`, error);\n }\n let referencedEntityId: string | undefined = undefined;\n let bootstrapMismatch = false;\n if (bootstrap.postFetch.type === 'single') {\n bootstrapMismatch = product.productType !== ProductType.Post;\n referencedEntityId = bootstrap.postFetch.postId;\n }\n if (bootstrap.postFetch.type === 'byCollection') {\n bootstrapMismatch = product.productType !== ProductType.Collection;\n referencedEntityId = bootstrap.postFetch.collectionId;\n }\n if (bootstrapMismatch) {\n this.log('warn', `Product type \"${product.productType}\" does not match URL \"${product.url}\" (${bootstrap.postFetch.type})`);\n }\n else if (referencedEntityId) {\n saveReferencedEntityId(referencedEntityId);\n }\n }\n else {\n this.log('error', `Product type is \"${product.productType}\" but could not create downloader from URL \"${product.url}\"`);\n }\n return;\n }\n this.log('debug', `Unknown product type \"${product.productType}\": no further action can be taken`);\n return;\n }\n\n #isFetchingMultipleProducts(productFetch: DownloaderConfig<Product>['productFetch']): productFetch is DownloaderConfig<Product>['productFetch'] & { type: 'byShop' } {\n return productFetch.type === 'byShop';\n }\n}\n"]}
1
+ {"version":3,"file":"ProductDownloader.js","sourceRoot":"","sources":["../../src/downloaders/ProductDownloader.ts"],"names":[],"mappings":"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","sourcesContent":["import ProductParser from '../parsers/ProductParser.js';\nimport URLHelper from '../utils/URLHelper.js';\nimport Downloader, { type DownloaderConfig, type DownloaderStartParams } from './Downloader.js';\nimport StatusCache from './cache/StatusCache.js';\nimport { generateProductSummary } from './templates/ProductInfo.js';\nimport path from 'path';\nimport { TargetSkipReason } from './DownloaderEvent.js';\nimport { ProductType, type Product } from '../entities/Product.js';\nimport { type Downloadable } from '../entities/Downloadable.js';\nimport Bootstrap from './Bootstrap.js';\nimport ProductsFetcher from './ProductsFetcher.js';\nimport { type ProductDirectories } from '../utils/FSHelper.js';\nimport { type DBInstance } from '../browse/db/index.js';\nimport { IncludeCriteriaHelper } from './IncludeCriteriaHelper.js';\n\nexport default class ProductDownloader extends Downloader<Product> {\n\n static version = '1.0.1';\n\n name = 'ProductDownloader';\n\n #startPromise: Promise<void> | null = null;\n\n doStart(params?: DownloaderStartParams): Promise<void> {\n\n if (this.#startPromise) {\n throw Error('Downloader already running');\n }\n\n this.#startPromise = this.#doStart(params)\n .finally(() => {\n this.#startPromise = null;\n });\n\n return this.#startPromise;\n }\n\n async #doStart(params?: DownloaderStartParams): Promise<void> {\n try {\n const { signal } = params || {};\n const productFetch = this.config.productFetch;\n const db = await this.db();\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n\n if (productFetch.type === 'byShop') {\n this.log('info', `Targeting products by '${productFetch.vanity}'`);\n }\n else if (productFetch.type === 'byFile') {\n this.log('info', `Target product given by API data in \"${productFetch.filePath}\"`);\n }\n else {\n this.log('info', `Targeting product #${productFetch.productId}`);\n }\n\n // Step 1: get products (if by shop) or target product\n const productsFetcher = new ProductsFetcher({\n config: this.config,\n fetcher: this.fetcher,\n logger: this.logger,\n signal\n });\n productsFetcher.on('statusChange', ({current}) => {\n if (current.status === 'running') {\n this.emit('fetchBegin', { targetType: productsFetcher.getTargetType() });\n }\n });\n productsFetcher.begin();\n\n // Step 2: download products in each fetched list\n let downloaded = 0;\n let skippedUnviewable = 0;\n let skippedRedundant = 0;\n let skippedPublishDateOutOfRange = 0;\n let campaignSaved = false;\n let stopConditionMet = false;\n const productsParser = new ProductParser(this.logger);\n while (productsFetcher.hasNext()) {\n const { list, aborted, error } = await productsFetcher.next();\n if (!list || aborted) {\n break;\n }\n if (!list && error) {\n this.emit('end', { aborted: false, error, message: 'ProductsFetcher error' });\n return;\n }\n if (!campaignSaved && list.items[0]?.campaign) {\n await this.saveCampaignInfo(list.items[0].campaign, signal);\n campaignSaved = true;\n if (this.checkAbortSignal(signal)) {\n return;\n }\n }\n\n for (const _product of list.items) {\n\n if (stopConditionMet) {\n break;\n }\n\n let product = _product;\n\n if (this.#isFetchingMultipleProducts(productFetch)) {\n // Refresh to ensure media links have not expired\n this.log('debug', `Refresh product #${_product.id}`);\n const productURL = URLHelper.constructProductAPIURL(product.id);\n const { json } = await this.commonFetchAPI(productURL, signal);\n let refreshed: Product | null = null;\n if (json) {\n refreshed = productsParser.parseProductsAPIResponse(json, productURL).items[0] || null;\n if (!refreshed) {\n this.log('warn', `Refreshed product #${_product.id} but got empty value - going to use existing data`);\n }\n else if (refreshed.id !== _product.id) {\n this.log('warn', `Refreshed product #${_product.id} but ID mismatch - going to use existing data`);\n refreshed = null;\n }\n else {\n // Refreshed data does not have productType - reinstate it\n refreshed.productType = _product.productType;\n }\n }\n else if (this.checkAbortSignal(signal)) {\n return;\n }\n else {\n this.log('warn', `Failed to refresh product #${_product.id} - going to use existing data`);\n }\n if (refreshed) {\n this.log('debug', `Use refreshed product data #${refreshed.id}`);\n product = refreshed;\n }\n }\n\n this.emit('targetBegin', { target: product });\n\n // Step 4.1: product directories\n const productDirs = this.fsHelper.getProductDirs(product);\n this.log('debug', 'Product directories:', productDirs);\n\n // Step 4.2: Check with status cache\n const statusCache = StatusCache.getInstance(this.config, productDirs.statusCache, this.logger);\n if (!product.productType || product.productType === ProductType.DigitalCommerce) {\n const statusCacheValidation = statusCache.validate(product, productDirs.root, this.config)\n if (!statusCacheValidation.invalidated) {\n this.log('info', `Skipped downloading product #${product.id}: already downloaded`);\n this.emit('targetEnd', {\n target: product,\n isSkipped: true,\n skipReason: TargetSkipReason.AlreadyDownloaded,\n skipMessage: 'Target already downloaded'\n });\n skippedRedundant++;\n if (this.config.stopOn === 'previouslyDownloaded') {\n stopConditionMet = true;\n }\n continue;\n }\n }\n \n switch ((await this.#doDownload(\n product,\n productDirs,\n statusCache,\n db,\n signal\n )).status) {\n case 'aborted':\n return;\n case 'downloaded':\n downloaded++;\n break;\n case 'skippedPublishDateOutOfRange':\n skippedPublishDateOutOfRange++;\n if (this.config.stopOn === 'postPublishDateOutOfRange' ||\n this.config.stopOn === 'publishDateOutOfRange'\n ) {\n stopConditionMet = true;\n }\n break;\n case 'skippedUnviewable':\n skippedUnviewable++;\n break;\n }\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n }\n\n if (stopConditionMet) {\n break;\n }\n }\n\n if (this.checkAbortSignal(signal)) {\n return;\n }\n\n if (stopConditionMet && this.#isFetchingMultipleProducts(productFetch)) {\n this.log('info', `Stop downloader: stop condition \"${this.config.stopOn}\" met`)\n }\n\n // Done\n if (productFetch.type === 'byShop') {\n this.log('info', `Done downloading products by '${productFetch.vanity}'`);\n }\n else if (productFetch.type === 'byFile') {\n this.log('info', `Done downloading product given in \"${productFetch.filePath}\"`);\n }\n else {\n this.log('info', `Done downloading product #${productFetch.productId}`);\n }\n let endMessage = 'Done';\n if (this.#isFetchingMultipleProducts(productFetch)) {\n const skippedStrParts: string[] = [];\n if (skippedUnviewable) {\n skippedStrParts.push(`${skippedUnviewable} unviewable`);\n }\n if (skippedRedundant) {\n skippedStrParts.push(`${skippedRedundant} redundant`);\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} / ${productsFetcher.getTotal()} products processed${skippedStr}`;\n this.log('info', endMessage);\n }\n this.emit('end', { aborted: false, message: endMessage });\n }\n finally {\n await this.closeDB();\n if (this.logger) {\n await this.logger.end();\n }\n }\n }\n\n async #doDownload(\n product: Product,\n productDirs: ProductDirectories,\n statusCache: StatusCache,\n db: DBInstance,\n signal?: AbortSignal\n ): Promise<{status: 'skippedUnviewable' | 'skippedPublishDateOutOfRange' | 'aborted' | 'downloaded' }> {\n // Step 1: Check whether we should download product\n const criteriaHelper = new IncludeCriteriaHelper(this.logger);\n const criteriaCheck = criteriaHelper.checkProduct(product, this.config);\n if (!criteriaCheck.ok) {\n switch (criteriaCheck.reason) {\n case 'unviewable': {\n this.log('warn', `Skipped downloading product #${product.id}: not accessible by current user`);\n this.emit('targetEnd', {\n target: product,\n isSkipped: true,\n skipReason: TargetSkipReason.Inaccessible,\n skipMessage: 'Target is not accessible by current user'\n });\n return {\n status: 'skippedUnviewable'\n };\n }\n case 'publishDateOutOfRange': {\n this.log('warn', `Skipped downloading product #${product.id}: publish date out of range`);\n this.emit('targetEnd', {\n target: product,\n isSkipped: true,\n skipReason: TargetSkipReason.PublishDateOutOfRange,\n skipMessage: 'Publish date out of range'\n });\n return {\n status: 'skippedPublishDateOutOfRange'\n };\n }\n }\n }\n\n if (!product.isAccessible && this.config.include.lockedContent) {\n this.log('warn', `Product #${product.id} is not accessible by current user`);\n }\n\n this.fsHelper.createDir(productDirs.root);\n\n // Step 2: save product info\n if (this.config.include.contentInfo) {\n this.log('info', 'Save product info');\n this.emit('phaseBegin', { target: product, phase: 'saveInfo' });\n this.fsHelper.createDir(productDirs.info);\n const summary = generateProductSummary(product);\n const summaryFile = path.resolve(productDirs.info, 'info.txt');\n const saveSummaryResult = await this.fsHelper.writeTextFile(summaryFile, summary, this.config.fileExistsAction.info);\n this.logWriteTextFileResult(saveSummaryResult, product, 'product summary');\n\n const productRawFile = path.resolve(productDirs.info, 'product-api.json');\n const saveProductRawResult = await this.fsHelper.writeTextFile(\n productRawFile, product.raw, this.config.fileExistsAction.infoAPI);\n this.logWriteTextFileResult(saveProductRawResult, product, 'product API data');\n this.emit('phaseEnd', { target: product, phase: 'saveInfo' });\n }\n\n // Step 3: Download product media items\n const incPreview = this.config.include.previewMedia;\n const incContent = this.config.include.contentMedia;\n this.log('info', 'Download:', {\n ['preview items']: incPreview === true ? 'yes' : incPreview === false ? 'no' : JSON.stringify(incPreview),\n ['content items']: incContent === true ? 'yes' : incContent === false ? 'no' : JSON.stringify(incContent)\n });\n\n const __inc = (inc: boolean | Array<any>, target: Downloadable) => {\n if (typeof inc === 'boolean') {\n return inc;\n }\n return (inc.includes(target.type as any));\n };\n\n const previewMedia = product.previewMedia.filter((tt) => __inc(incPreview, tt));\n const contentMedia = product.contentMedia.filter((tt) => __inc(incContent, tt));\n const campaignDir = product.campaign ? this.fsHelper.getCampaignDirs(product.campaign).root : null;\n\n if (this.config.include.previewMedia || this.config.include.contentMedia) {\n this.emit('phaseBegin', { target: product, phase: 'saveMedia' });\n const batchResult = await this.createDownloadTaskBatch(\n `Product #${product.id} (${product.name})`,\n signal,\n\n previewMedia.length > 0 ? {\n target: previewMedia,\n targetName: `product #${product.id} -> preview media`,\n src: product,\n dirs: {\n campaign: campaignDir,\n main: productDirs.previewMedia,\n thumbnails: productDirs.thumbnails\n },\n fileExistsAction: this.config.fileExistsAction.content\n } : null,\n\n contentMedia.length > 0 ? {\n target: contentMedia,\n targetName: `product #${product.id} -> content media`,\n src: product,\n dirs: {\n campaign: campaignDir,\n main: productDirs.contentMedia,\n thumbnails: productDirs.thumbnails\n },\n fileExistsAction: this.config.fileExistsAction.content,\n ignoreCreateTaskErrors: !product.isAccessible\n } : null\n );\n\n const { batch } = batchResult;\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 try {\n if (this.checkAbortSignal(signal)) {\n return {\n status: 'aborted'\n };\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: product, phase: 'batchDownload', batch });\n\n await batch.start();\n\n // Step 4: Update status cache\n statusCache.updateOnDownload(product, productDirs.root, batch.getTasks('error').length > 0 || batchResult.errorCount > 0, this.config);\n\n await batch.destroy();\n }\n finally {\n if (signal) {\n signal.removeEventListener('abort', abortHandler);\n }\n }\n\n this.emit('phaseEnd', { target: product, phase: 'batchDownload' });\n this.emit('phaseEnd', { target: product, phase: 'saveMedia' });\n }\n\n if (this.checkAbortSignal(signal)) {\n return {\n status: 'aborted'\n };\n }\n\n // Step 5: Save to DB\n let skipDB = false;\n let saveReferencedEntityId: (id: string) => void;\n const dbProduct = db.getContent(product.id, 'product');\n if (!product.isAccessible) {\n // Skip if existing db record (if any) refers to accessible product\n skipDB = dbProduct !== null && dbProduct.isAccessible;\n }\n if (!skipDB) {\n db.saveContent(product);\n saveReferencedEntityId = (id) => {\n product.referencedEntityId = id;\n db.saveContent(product);\n };\n }\n else {\n this.log('info', `Skip overwrite existing accessible product #${product.id} in DB with current unviewable version`);\n saveReferencedEntityId = (id) => {\n // Ensure referencedEntityId is updated\n dbProduct!.referencedEntityId = id;\n db.saveContent(dbProduct!);\n };\n }\n\n // Step 6: check product type and download referenced product / collection\n await this.#processProductType(product, saveReferencedEntityId, signal);\n\n // Done\n this.log('info', `Done downloading product #${product.id}`);\n this.emit('targetEnd', { target: product, isSkipped: false });\n\n return {\n status: 'downloaded'\n }\n }\n\n // Carry out further action based on product type\n async #processProductType(\n product: Product,\n saveReferencedEntityId: (id: string) => void,\n signal?: AbortSignal\n ) {\n if (product.productType === undefined || product.productType === ProductType.DigitalCommerce) {\n this.log('debug', `Product type \"${product.productType}\": no further action required`);\n return;\n }\n if (product.productType === ProductType.Post || product.productType === ProductType.Collection) {\n const bootstrap = Bootstrap.getDownloaderBootstrapDataByURL(product.url);\n if (bootstrap?.type === 'post') {\n this.log('info', `Download ${product.productType} referenced by product: ${product.url}`);\n const PostDownloader = (await import('./PostDownloader.js')).default;\n const downloader = new PostDownloader(\n {\n ...this.config,\n ...bootstrap\n },\n this.db,\n this.logger,\n {\n skipSaveCampaign: true,\n keepLoggerOpen: true,\n keepDBOpen: true\n }\n );\n try {\n await downloader.start({ signal });\n this.log('info', `Done downloading ${product.productType} referenced by product`);\n }\n catch (error) {\n if (signal?.aborted) {\n throw error;\n }\n this.log('error', `Error downloading ${product.productType} referenced by product (url: ${product.url})`, error);\n }\n let referencedEntityId: string | undefined = undefined;\n let bootstrapMismatch = false;\n if (bootstrap.postFetch.type === 'single') {\n bootstrapMismatch = product.productType !== ProductType.Post;\n referencedEntityId = bootstrap.postFetch.postId;\n }\n if (bootstrap.postFetch.type === 'byCollection') {\n bootstrapMismatch = product.productType !== ProductType.Collection;\n referencedEntityId = bootstrap.postFetch.collectionId;\n }\n if (bootstrapMismatch) {\n this.log('warn', `Product type \"${product.productType}\" does not match URL \"${product.url}\" (${bootstrap.postFetch.type})`);\n }\n else if (referencedEntityId) {\n saveReferencedEntityId(referencedEntityId);\n }\n }\n else {\n this.log('error', `Product type is \"${product.productType}\" but could not create downloader from URL \"${product.url}\"`);\n }\n return;\n }\n this.log('debug', `Unknown product type \"${product.productType}\": no further action can be taken`);\n return;\n }\n\n #isFetchingMultipleProducts(productFetch: DownloaderConfig<Product>['productFetch']): productFetch is DownloaderConfig<Product>['productFetch'] & { type: 'byShop' } {\n return productFetch.type === 'byShop';\n }\n}\n"]}
@@ -14,6 +14,7 @@ export default class M3U8DownloadTask extends FFmpegDownloadTaskBase<VideoMediaI
14
14
  #private;
15
15
  name: string;
16
16
  constructor(params: M3U8DownloadTaskParams);
17
+ start(): Promise<void>;
17
18
  protected resolveDestPath(signal?: AbortSignal): Promise<string>;
18
19
  protected getFFmpegCommandParams(signal?: AbortSignal): Promise<M3U8FFmpegCommandParams>;
19
20
  protected getTargetDuration(): number | null;
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _M3U8DownloadTask_instances, _M3U8DownloadTask_fetcher, _M3U8DownloadTask_unresolvedDestFilePath, _M3U8DownloadTask_ffmpegCommandParams, _M3U8DownloadTask_pickVariant;
12
+ var _M3U8DownloadTask_instances, _M3U8DownloadTask_skipOnStart, _M3U8DownloadTask_fetcher, _M3U8DownloadTask_unresolvedDestFilePath, _M3U8DownloadTask_ffmpegCommandParams, _M3U8DownloadTask_pickVariant, _M3U8DownloadTask_getProtectionStatus, _M3U8DownloadTask_getResolutionString;
13
13
  import { SITE_URL } from '../../utils/URLHelper.js';
14
14
  import FFmpegDownloadTaskBase from './FFmpegDownloadTaskBase.js';
15
15
  import semver from 'semver';
@@ -22,12 +22,23 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
22
22
  super(params);
23
23
  _M3U8DownloadTask_instances.add(this);
24
24
  this.name = 'M3U8DownloadTask';
25
+ _M3U8DownloadTask_skipOnStart.set(this, void 0);
25
26
  _M3U8DownloadTask_fetcher.set(this, void 0);
26
27
  _M3U8DownloadTask_unresolvedDestFilePath.set(this, void 0);
27
28
  _M3U8DownloadTask_ffmpegCommandParams.set(this, void 0);
28
29
  __classPrivateFieldSet(this, _M3U8DownloadTask_fetcher, params.fetcher, "f");
29
30
  __classPrivateFieldSet(this, _M3U8DownloadTask_unresolvedDestFilePath, params.destFilePath, "f");
30
31
  __classPrivateFieldSet(this, _M3U8DownloadTask_ffmpegCommandParams, null, "f");
32
+ __classPrivateFieldSet(this, _M3U8DownloadTask_skipOnStart, null, "f");
33
+ }
34
+ async start() {
35
+ if (__classPrivateFieldGet(this, _M3U8DownloadTask_skipOnStart, "f")) {
36
+ return this.notifySkip({
37
+ name: 'other',
38
+ message: __classPrivateFieldGet(this, _M3U8DownloadTask_skipOnStart, "f").reason,
39
+ });
40
+ }
41
+ return await super.start();
31
42
  }
32
43
  async resolveDestPath(signal) {
33
44
  const params = await this.getFFmpegCommandParams(signal);
@@ -38,6 +49,11 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
38
49
  return __classPrivateFieldGet(this, _M3U8DownloadTask_ffmpegCommandParams, "f");
39
50
  }
40
51
  const input = await __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_pickVariant).call(this, signal);
52
+ if (input.src === null) {
53
+ __classPrivateFieldSet(this, _M3U8DownloadTask_skipOnStart, {
54
+ reason: `No downloadable source - ${input.reason}`
55
+ }, "f");
56
+ }
41
57
  const inputOptions = [
42
58
  '-protocol_whitelist',
43
59
  'http,https,tcp,tls',
@@ -49,22 +65,38 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
49
65
  inputOptions.push('-extension_picky', '0');
50
66
  }
51
67
  let output = __classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f");
52
- if (this.config.maxVideoResolution && this.config.maxVideoResolution > 0) {
53
- if (input.resolution) {
54
- this.log('info', `Video found with resolution at or below specified max resolution "${this.config.maxVideoResolution}": ${input.resolution}`);
55
- const { name: filename, ext, dir } = path.parse(__classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f"));
56
- output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename} (${input.resolution})${ext}`));
57
- }
58
- else {
59
- this.log('info', `No video found with resolution at or below specified max resolution "${this.config.maxVideoResolution}" - going to download highest`);
68
+ if (input.src) {
69
+ if (input.protected || input.resolution) {
70
+ const parsedFilePath = path.parse(__classPrivateFieldGet(this, _M3U8DownloadTask_unresolvedDestFilePath, "f"));
71
+ const { ext, dir } = parsedFilePath;
72
+ let filename = parsedFilePath.name;
73
+ if (input.resolution) {
74
+ filename += ` (${input.resolution})`;
75
+ }
76
+ if (input.protected) {
77
+ filename += ' drm';
78
+ }
79
+ output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename}${ext}`));
60
80
  }
81
+ const criteria = JSON.stringify({
82
+ 'max video resolution': this.config.maxVideoResolution || undefined,
83
+ 'include protected media': this.config.include.protectedMedia ? 'yes' : undefined
84
+ });
85
+ const criteriaStr = criteria === '{}' ? '' : ` matching criteria ${JSON.stringify(criteria)}`;
86
+ const streamInfoParts = [
87
+ input.resolution || 'unknown resolution',
88
+ input.protected === true ? 'protected'
89
+ : input.protected === false ? ''
90
+ : 'unknown'
91
+ ];
92
+ this.log('info', `Target stream${criteriaStr}:`, streamInfoParts.join(';'));
61
93
  }
62
94
  __classPrivateFieldSet(this, _M3U8DownloadTask_ffmpegCommandParams, {
63
95
  inputs: [
64
96
  {
65
- input: input.src,
97
+ input: input.src || this.src,
66
98
  options: inputOptions,
67
- resolution: input.resolution
99
+ resolution: input.src ? input.resolution : null
68
100
  }
69
101
  ],
70
102
  output
@@ -75,54 +107,120 @@ class M3U8DownloadTask extends FFmpegDownloadTaskBase {
75
107
  return this.srcEntity.duration;
76
108
  }
77
109
  }
78
- _M3U8DownloadTask_fetcher = new WeakMap(), _M3U8DownloadTask_unresolvedDestFilePath = new WeakMap(), _M3U8DownloadTask_ffmpegCommandParams = new WeakMap(), _M3U8DownloadTask_instances = new WeakSet(), _M3U8DownloadTask_pickVariant = async function _M3U8DownloadTask_pickVariant(signal) {
79
- const maxResolution = this.config.maxVideoResolution;
80
- if (!maxResolution || maxResolution < 0) {
81
- return {
82
- src: this.src,
83
- resolution: null
84
- };
85
- }
86
- this.log('debug', `Apply maxVideoResolution "${maxResolution}"`);
87
- const { html: m3u8 } = await __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
110
+ _M3U8DownloadTask_skipOnStart = new WeakMap(), _M3U8DownloadTask_fetcher = new WeakMap(), _M3U8DownloadTask_unresolvedDestFilePath = new WeakMap(), _M3U8DownloadTask_ffmpegCommandParams = new WeakMap(), _M3U8DownloadTask_instances = new WeakSet(), _M3U8DownloadTask_pickVariant = async function _M3U8DownloadTask_pickVariant(signal) {
111
+ const { contents: m3u8 } = await __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
88
112
  url: this.src,
89
- type: 'html',
113
+ type: 'm3u8',
90
114
  maxRetries: this.config.request.maxRetries,
91
115
  signal
92
116
  });
93
117
  const parser = new m3u8Parser.Parser();
94
118
  parser.push(m3u8);
95
119
  parser.end();
96
- const variants = parser.manifest.playlists;
97
- this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);
98
- if (!variants) {
99
- this.log('debug', `No videos found in m3u8 manifest - going to download video with highest resolution`);
120
+ if (!parser.manifest.playlists || parser.manifest.playlists.length === 0) {
121
+ this.log('warn', `No stream found in m3u8 manifest - going to download without stream selection`);
122
+ return {
123
+ src: this.src,
124
+ resolution: 'best quality',
125
+ protected: undefined
126
+ };
127
+ }
128
+ const orderedPlaylistItems = parser.manifest.playlists
129
+ .sort((a, b) => {
130
+ // 1. Get heights or 0
131
+ const heightA = a.attributes.RESOLUTION?.height || 0;
132
+ const heightB = b.attributes.RESOLUTION?.height || 0;
133
+ // 2. If heights are different, sort by height
134
+ if (heightB !== heightA) {
135
+ return heightB - heightA;
136
+ }
137
+ // 3. If heights are the same (or both 0), sort by BANDWIDTH
138
+ const bandwidthA = a.attributes.BANDWIDTH || 0;
139
+ const bandwidthB = b.attributes.BANDWIDTH || 0;
140
+ return bandwidthB - bandwidthA;
141
+ });
142
+ let variants = await __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getProtectionStatus).call(this, orderedPlaylistItems, signal);
143
+ if (variants.every((v) => v.protected === false) && !this.config.maxVideoResolution) {
100
144
  return {
101
145
  src: this.src,
102
- resolution: null
146
+ resolution: 'best quality',
147
+ protected: false
103
148
  };
104
149
  }
150
+ this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);
151
+ // include.protectedMedia
152
+ if (!this.config.include.protectedMedia) {
153
+ const prevLength = variants.length;
154
+ variants = variants.filter((v) => !v.protected);
155
+ if (variants.length === 0) {
156
+ this.log('debug', 'All streams are protected');
157
+ return {
158
+ src: null,
159
+ reason: 'Media is protected'
160
+ };
161
+ }
162
+ else {
163
+ this.log('debug', `${(prevLength - variants.length) / prevLength} streams are protected`);
164
+ }
165
+ }
166
+ // maxVideoResolution
167
+ const maxResolution = this.config.maxVideoResolution;
168
+ const hasMaxResolutionConfigured = maxResolution && maxResolution > 0;
105
169
  const __hasAudio = (variant) => {
106
170
  const codecs = variant.attributes.CODECS || "";
107
171
  return codecs.includes("mp4a"); // crude check for AAC audio
108
172
  };
109
173
  const allWithoutAudio = variants.every((v) => !__hasAudio(v));
110
- const filtered = variants
111
- .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)))
112
- .sort((a, b) => b.attributes.RESOLUTION.height - a.attributes.RESOLUTION.height);
113
- if (filtered.length === 0) {
114
- this.log('debug', `No video found in m3u8 manifest at or below resolution "${maxResolution}" - going to download video with highest resolution`);
115
- return {
116
- src: this.src,
117
- resolution: null
118
- };
174
+ if (hasMaxResolutionConfigured) {
175
+ this.log('debug', `Apply maxVideoResolution "${maxResolution}"`);
176
+ const maxResCandidates = variants
177
+ .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)));
178
+ if (maxResCandidates.length === 0) {
179
+ this.log('debug', `No stream in m3u8 manifest has resolution "${maxResolution}" or lower - maxVideoResolution not applied`);
180
+ }
181
+ else {
182
+ variants = maxResCandidates;
183
+ }
119
184
  }
120
- const selected = filtered[0];
121
- this.log('debug', `Video at or below resolution "${maxResolution}": ${selected.uri} (${JSON.stringify(selected.attributes.RESOLUTION)})`);
185
+ const selected = variants[0];
122
186
  return {
123
187
  src: new URL(selected.uri, this.src).href,
124
- resolution: `${selected.attributes.RESOLUTION.width}x${selected.attributes.RESOLUTION.height}`
188
+ resolution: __classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getResolutionString).call(this, selected),
189
+ protected: selected.protected
125
190
  };
191
+ }, _M3U8DownloadTask_getProtectionStatus = async function _M3U8DownloadTask_getProtectionStatus(variants, signal) {
192
+ return await Promise.all(variants.map((variant) => __classPrivateFieldGet(this, _M3U8DownloadTask_fetcher, "f").get({
193
+ url: variant.uri,
194
+ type: 'm3u8',
195
+ maxRetries: this.config.request.maxRetries,
196
+ signal
197
+ })
198
+ .then(({ contents: m3u8 }) => {
199
+ const parser = new m3u8Parser.Parser();
200
+ parser.push(m3u8);
201
+ parser.end();
202
+ const protectionData = parser.manifest.contentProtection;
203
+ const _protected = !!(protectionData && typeof protectionData === 'object' && Object.entries(protectionData).length > 0);
204
+ return {
205
+ ...variant,
206
+ protected: _protected
207
+ };
208
+ })
209
+ .catch((error) => {
210
+ if (signal?.aborted) {
211
+ throw error;
212
+ }
213
+ this.log('warn', `Could not determine if stream (${__classPrivateFieldGet(this, _M3U8DownloadTask_instances, "m", _M3U8DownloadTask_getResolutionString).call(this, variant)}) is protected:`, error);
214
+ return {
215
+ ...variant,
216
+ protected: undefined
217
+ };
218
+ })));
219
+ }, _M3U8DownloadTask_getResolutionString = function _M3U8DownloadTask_getResolutionString(item) {
220
+ if (item.attributes.RESOLUTION?.width && item.attributes.RESOLUTION?.height) {
221
+ return `${item.attributes.RESOLUTION.width}x${item.attributes.RESOLUTION.height}`;
222
+ }
223
+ return null;
126
224
  };
127
225
  export default M3U8DownloadTask;
128
226
  //# sourceMappingURL=M3U8DownloadTask.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"M3U8DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/M3U8DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { type VideoMediaItem } from '../../entities/MediaItem.js';\nimport { SITE_URL } from '../../utils/URLHelper.js';\nimport FFmpegDownloadTaskBase, { type FFmpegCommandParams, type FFmpegDownloadTaskBaseParams } from './FFmpegDownloadTaskBase.js';\nimport semver from 'semver';\nimport m3u8Parser from 'm3u8-parser';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport path from 'path';\n\nexport interface M3U8DownloadTaskParams extends FFmpegDownloadTaskBaseParams<VideoMediaItem> {\n fetcher: Fetcher;\n destFilePath: string;\n}\n\ninterface M3U8FFmpegCommandParams extends FFmpegCommandParams {\n inputs: (FFmpegCommandParams['inputs'][number] & { resolution: string | null })[];\n};\n\nexport default class M3U8DownloadTask extends FFmpegDownloadTaskBase<VideoMediaItem> {\n\n name = 'M3U8DownloadTask';\n\n #fetcher: Fetcher;\n #unresolvedDestFilePath: string;\n #ffmpegCommandParams: M3U8FFmpegCommandParams | null;\n\n constructor(params: M3U8DownloadTaskParams) {\n super(params);\n this.#fetcher = params.fetcher;\n this.#unresolvedDestFilePath = params.destFilePath;\n this.#ffmpegCommandParams = null;\n }\n\n protected async resolveDestPath(signal?: AbortSignal) {\n const params = await this.getFFmpegCommandParams(signal);\n return params.output;\n }\n\n protected async getFFmpegCommandParams(signal?: AbortSignal): Promise<M3U8FFmpegCommandParams> {\n if (this.#ffmpegCommandParams) {\n return this.#ffmpegCommandParams;\n }\n const input = await this.#pickVariant(signal);\n const inputOptions = [\n '-protocol_whitelist',\n 'http,https,tcp,tls',\n '-headers',\n `Referer: ${SITE_URL}`\n ];\n // `extension_picky` introduced in v7.1.1\n if (semver.satisfies(this.getFFmpegVersion(), '>=7.1.1')) {\n inputOptions.push('-extension_picky', '0');\n }\n let output = this.#unresolvedDestFilePath;\n if (this.config.maxVideoResolution && this.config.maxVideoResolution > 0) {\n if (input.resolution) {\n this.log('info', `Video found with resolution at or below specified max resolution \"${this.config.maxVideoResolution}\": ${input.resolution}`);\n const { name: filename, ext, dir } = path.parse(this.#unresolvedDestFilePath);\n output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename} (${input.resolution})${ext}`));\n }\n else {\n this.log('info', `No video found with resolution at or below specified max resolution \"${this.config.maxVideoResolution}\" - going to download highest`);\n }\n }\n\n this.#ffmpegCommandParams = {\n inputs: [\n {\n input: input.src,\n options: inputOptions,\n resolution: input.resolution\n }\n ],\n output\n };\n\n return this.#ffmpegCommandParams;\n }\n\n protected getTargetDuration(): number | null {\n return this.srcEntity.duration;\n }\n\n async #pickVariant(signal?: AbortSignal) {\n const maxResolution = this.config.maxVideoResolution;\n if (!maxResolution || maxResolution < 0) {\n return {\n src: this.src,\n resolution: null\n };\n }\n\n this.log('debug', `Apply maxVideoResolution \"${maxResolution}\"`);\n\n const { html: m3u8 } = await this.#fetcher.get({\n url: this.src,\n type: 'html',\n maxRetries: this.config.request.maxRetries,\n signal\n });\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n\n const variants = parser.manifest.playlists;\n\n this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);\n \n if (!variants) {\n this.log('debug', `No videos found in m3u8 manifest - going to download video with highest resolution`);\n return {\n src: this.src,\n resolution: null\n };\n }\n \n const __hasAudio = (variant: typeof variants[number]) => {\n const codecs = variant.attributes.CODECS || \"\";\n return codecs.includes(\"mp4a\"); // crude check for AAC audio\n }\n\n const allWithoutAudio = variants.every((v) => !__hasAudio(v));\n\n const filtered = variants\n .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)))\n .sort((a, b) => b.attributes.RESOLUTION!.height - a.attributes.RESOLUTION!.height);\n\n if (filtered.length === 0) {\n this.log('debug', `No video found in m3u8 manifest at or below resolution \"${maxResolution}\" - going to download video with highest resolution`);\n return {\n src: this.src,\n resolution: null\n };\n }\n\n const selected = filtered[0];\n\n this.log('debug', `Video at or below resolution \"${maxResolution}\": ${selected.uri} (${JSON.stringify(selected.attributes.RESOLUTION)})`);\n\n return {\n src: new URL(selected.uri, this.src).href,\n resolution: `${selected.attributes.RESOLUTION!.width}x${selected.attributes.RESOLUTION!.height}`\n };\n }\n}\n"]}
1
+ {"version":3,"file":"M3U8DownloadTask.js","sourceRoot":"","sources":["../../../src/downloaders/task/M3U8DownloadTask.ts"],"names":[],"mappings":"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","sourcesContent":["import { type VideoMediaItem } from '../../entities/MediaItem.js';\nimport { SITE_URL } from '../../utils/URLHelper.js';\nimport FFmpegDownloadTaskBase, { type FFmpegCommandParams, type FFmpegDownloadTaskBaseParams } from './FFmpegDownloadTaskBase.js';\nimport semver from 'semver';\nimport m3u8Parser, { type PlaylistItem } from 'm3u8-parser';\nimport type Fetcher from '../../utils/Fetcher.js';\nimport FSHelper from '../../utils/FSHelper.js';\nimport path from 'path';\n\ninterface Variant extends PlaylistItem {\n protected?: boolean;\n}\n\ntype PickVariantResult = {\n src: null;\n reason: string;\n} | {\n src: string;\n resolution: string | null,\n protected?: boolean;\n};\n\nexport interface M3U8DownloadTaskParams extends FFmpegDownloadTaskBaseParams<VideoMediaItem> {\n fetcher: Fetcher;\n destFilePath: string;\n}\n\ninterface M3U8FFmpegCommandParams extends FFmpegCommandParams {\n inputs: (FFmpegCommandParams['inputs'][number] & { resolution: string | null })[];\n};\n\nexport default class M3U8DownloadTask extends FFmpegDownloadTaskBase<VideoMediaItem> {\n\n name = 'M3U8DownloadTask';\n\n #skipOnStart: {\n reason: string;\n } | null;\n #fetcher: Fetcher;\n #unresolvedDestFilePath: string;\n #ffmpegCommandParams: M3U8FFmpegCommandParams | null;\n\n constructor(params: M3U8DownloadTaskParams) {\n super(params);\n this.#fetcher = params.fetcher;\n this.#unresolvedDestFilePath = params.destFilePath;\n this.#ffmpegCommandParams = null;\n this.#skipOnStart = null;\n }\n\n async start() {\n if (this.#skipOnStart) {\n return this.notifySkip({\n name: 'other',\n message: this.#skipOnStart.reason,\n });\n }\n return await super.start();\n }\n\n protected async resolveDestPath(signal?: AbortSignal) {\n const params = await this.getFFmpegCommandParams(signal);\n return params.output;\n }\n\n protected async getFFmpegCommandParams(signal?: AbortSignal): Promise<M3U8FFmpegCommandParams> {\n if (this.#ffmpegCommandParams) {\n return this.#ffmpegCommandParams;\n }\n\n const input = await this.#pickVariant(signal);\n if (input.src === null) {\n this.#skipOnStart = {\n reason: `No downloadable source - ${input.reason}`\n };\n }\n\n const inputOptions = [\n '-protocol_whitelist',\n 'http,https,tcp,tls',\n '-headers',\n `Referer: ${SITE_URL}`\n ];\n // `extension_picky` introduced in v7.1.1\n if (semver.satisfies(this.getFFmpegVersion(), '>=7.1.1')) {\n inputOptions.push('-extension_picky', '0');\n }\n let output = this.#unresolvedDestFilePath;\n if (input.src) {\n if (input.protected || input.resolution) {\n const parsedFilePath = path.parse(this.#unresolvedDestFilePath);\n const { ext, dir } = parsedFilePath;\n let filename = parsedFilePath.name;\n if (input.resolution) {\n filename += ` (${input.resolution})`;\n }\n if (input.protected) {\n filename += ' drm';\n }\n output = path.resolve(dir, FSHelper.sanitizeFilename(`${filename}${ext}`));\n }\n\n const criteria = JSON.stringify({\n 'max video resolution': this.config.maxVideoResolution || undefined,\n 'include protected media': this.config.include.protectedMedia ? 'yes' : undefined\n });\n const criteriaStr = criteria === '{}' ? '' : ` matching criteria ${JSON.stringify(criteria)}`;\n const streamInfoParts = [\n input.resolution || 'unknown resolution',\n input.protected === true ? 'protected'\n : input.protected === false ? ''\n : 'unknown'\n ];\n this.log('info', `Target stream${criteriaStr}:`, streamInfoParts.join(';'));\n }\n\n this.#ffmpegCommandParams = {\n inputs: [\n {\n input: input.src || this.src,\n options: inputOptions,\n resolution: input.src ? input.resolution : null\n }\n ],\n output\n };\n\n return this.#ffmpegCommandParams;\n }\n\n protected getTargetDuration(): number | null {\n return this.srcEntity.duration;\n }\n\n async #pickVariant(signal?: AbortSignal): Promise<PickVariantResult> {\n const { contents: m3u8 } = await this.#fetcher.get({\n url: this.src,\n type: 'm3u8',\n maxRetries: this.config.request.maxRetries,\n signal\n });\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n\n if (!parser.manifest.playlists || parser.manifest.playlists.length === 0) {\n this.log('warn', `No stream found in m3u8 manifest - going to download without stream selection`);\n return {\n src: this.src,\n resolution: 'best quality',\n protected: undefined\n };\n }\n\n const orderedPlaylistItems = parser.manifest.playlists\n .sort((a, b) => {\n // 1. Get heights or 0\n const heightA = a.attributes.RESOLUTION?.height || 0;\n const heightB = b.attributes.RESOLUTION?.height || 0;\n\n // 2. If heights are different, sort by height\n if (heightB !== heightA) {\n return heightB - heightA;\n }\n\n // 3. If heights are the same (or both 0), sort by BANDWIDTH\n const bandwidthA = a.attributes.BANDWIDTH || 0;\n const bandwidthB = b.attributes.BANDWIDTH || 0;\n\n return bandwidthB - bandwidthA;\n });\n let variants = await this.#getProtectionStatus(orderedPlaylistItems, signal);\n \n if (variants.every((v) => v.protected === false) && !this.config.maxVideoResolution) {\n return {\n src: this.src,\n resolution: 'best quality',\n protected: false\n };\n }\n\n this.log('debug', `m3u8 has ${variants?.length ?? 0} variants (src: ${this.src})`);\n \n // include.protectedMedia\n if (!this.config.include.protectedMedia) {\n const prevLength = variants.length;\n variants = variants.filter((v) => !v.protected)\n if (variants.length === 0) {\n this.log('debug', 'All streams are protected');\n return {\n src: null,\n reason: 'Media is protected'\n };\n }\n else {\n this.log('debug', `${(prevLength - variants.length) / prevLength} streams are protected`)\n }\n }\n\n // maxVideoResolution\n const maxResolution = this.config.maxVideoResolution;\n const hasMaxResolutionConfigured = maxResolution && maxResolution > 0;\n \n const __hasAudio = (variant: typeof variants[number]) => {\n const codecs = variant.attributes.CODECS || \"\";\n return codecs.includes(\"mp4a\"); // crude check for AAC audio\n }\n\n const allWithoutAudio = variants.every((v) => !__hasAudio(v));\n\n if (hasMaxResolutionConfigured) {\n this.log('debug', `Apply maxVideoResolution \"${maxResolution}\"`);\n const maxResCandidates = variants\n .filter((v) => (v.attributes.RESOLUTION && v.attributes.RESOLUTION.height <= maxResolution) && (allWithoutAudio || __hasAudio(v)));\n if (maxResCandidates.length === 0 ) {\n this.log('debug', `No stream in m3u8 manifest has resolution \"${maxResolution}\" or lower - maxVideoResolution not applied`);\n }\n else {\n variants = maxResCandidates;\n }\n }\n\n const selected = variants[0];\n\n return {\n src: new URL(selected.uri, this.src).href,\n resolution: this.#getResolutionString(selected),\n protected: selected.protected\n };\n }\n\n async #getProtectionStatus(variants: PlaylistItem[], signal?: AbortSignal): Promise<Variant[]> {\n return await Promise.all(variants.map((variant) =>\n this.#fetcher.get({\n url: variant.uri,\n type: 'm3u8',\n maxRetries: this.config.request.maxRetries,\n signal\n })\n .then(({contents: m3u8}) => {\n const parser = new m3u8Parser.Parser();\n parser.push(m3u8);\n parser.end();\n const protectionData = parser.manifest.contentProtection;\n const _protected = !!(protectionData && typeof protectionData === 'object' && Object.entries(protectionData).length > 0);\n return {\n ...variant,\n protected: _protected\n };\n })\n .catch((error: unknown) => {\n if (signal?.aborted) {\n throw error;\n }\n this.log('warn', `Could not determine if stream (${this.#getResolutionString(variant)}) is protected:`, error);\n return {\n ...variant,\n protected: undefined\n };\n })\n ));\n }\n\n #getResolutionString(item: PlaylistItem) {\n if (item.attributes.RESOLUTION?.width && item.attributes.RESOLUTION?.height) {\n return `${item.attributes.RESOLUTION.width}x${item.attributes.RESOLUTION.height}`;\n }\n return null;\n }\n}\n"]}
@@ -321,9 +321,7 @@ _a = YouTubeDownloadTask, _YouTubeDownloadTask_destDir = new WeakMap(), _YouTube
321
321
  return (await InnertubeLoader.getInstance(this.config)).getVideoInfo(videoId);
322
322
  }, _YouTubeDownloadTask_resolveURL = async function _YouTubeDownloadTask_resolveURL(url) {
323
323
  if (!__classPrivateFieldGet(_a, _a, "f", _YouTubeDownloadTask_innertubeForResolveURL)) {
324
- __classPrivateFieldSet(_a, _a, await InnertubeLib.Innertube.create({
325
- player_id: '9f4cc5e4'
326
- }), "f", _YouTubeDownloadTask_innertubeForResolveURL);
324
+ __classPrivateFieldSet(_a, _a, await InnertubeLib.Innertube.create(), "f", _YouTubeDownloadTask_innertubeForResolveURL);
327
325
  }
328
326
  const innertube = __classPrivateFieldGet(_a, _a, "f", _YouTubeDownloadTask_innertubeForResolveURL);
329
327
  const endpoint = await innertube.resolveURL(url);