diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 75d439a..b07164f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.51.0" + ".": "0.50.4" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c54db..7075a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,5 @@ # Changelog -## [0.51.0](https://github.com/webstreamr/webstreamr/compare/v0.50.4...v0.51.0) (2025-09-13) - - -### Miscellaneous Chores - -* **fetcher:** log more infos about responses ([731bd33](https://github.com/webstreamr/webstreamr/commit/731bd334703f0592f083d4264c382c28acf68eb1)) -* **fetcher:** log proxy usage, allow to disable proxy via config ([b67e1b8](https://github.com/webstreamr/webstreamr/commit/b67e1b85d634be413e004625b13403acc445ce72)) -* log error count in final result log ([2421913](https://github.com/webstreamr/webstreamr/commit/24219135874be6cdecdb95cabfed86ad60d6c451)) - - -### Features - -* allow to disable cache ([4078f0c](https://github.com/webstreamr/webstreamr/commit/4078f0c404a76f4c5a3c12ec74108d3d03840e34)) - - -### Reverts - -* Revert "fix(extractor): use SuperVideo embed URls which are not triggering CF challenges" ([4351ddc](https://github.com/webstreamr/webstreamr/commit/4351ddc00dc349bde60968dc1490359e4720f924)) - ## [0.50.4](https://github.com/webstreamr/webstreamr/compare/v0.50.3...v0.50.4) (2025-09-12) diff --git a/package-lock.json b/package-lock.json index e6a2171..6f59579 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "webstreamr", - "version": "0.51.0", + "version": "0.50.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "webstreamr", - "version": "0.51.0", + "version": "0.50.4", "license": "MIT", "dependencies": { "@keyv/sqlite": "^4.0.5", diff --git a/package.json b/package.json index a9c6544..bd522cd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "webstreamr", "description": "Provides HTTP URLs from streaming websites.", - "version": "0.51.0", + "version": "0.50.4", "type": "commonjs", "scripts": { "analyse": "tsc --noEmit --project ./tsconfig.dev.json", diff --git a/src/extractor/ExtractorRegistry.ts b/src/extractor/ExtractorRegistry.ts index 96a45ad..b65bc83 100644 --- a/src/extractor/ExtractorRegistry.ts +++ b/src/extractor/ExtractorRegistry.ts @@ -3,7 +3,7 @@ import KeyvSqlite from '@keyv/sqlite'; import { Cacheable, CacheableMemory, Keyv } from 'cacheable'; import winston from 'winston'; import { Context, Meta, UrlResult } from '../types'; -import { getCacheDir, isExtractorDisabled, noCache } from '../utils'; +import { getCacheDir, isExtractorDisabled } from '../utils'; import { Extractor } from './Extractor'; export class ExtractorRegistry { @@ -41,7 +41,7 @@ export class ExtractorRegistry { const storedDataRaw = await this.urlResultCache.getRaw(cacheKey); const expires = storedDataRaw?.expires; - if (storedDataRaw && expires && !noCache(ctx.config)) { + if (storedDataRaw && expires) { // Ignore the cache randomly after at least 2/3 of the TTL passed to start refreshing results slowly const refreshTimestamp = this.randomInteger(expires - extractor.ttl * (2 / 3), expires); const now = Date.now(); diff --git a/src/extractor/SuperVideo.ts b/src/extractor/SuperVideo.ts index 8e71a3f..65deca6 100644 --- a/src/extractor/SuperVideo.ts +++ b/src/extractor/SuperVideo.ts @@ -1,5 +1,3 @@ -import bytes from 'bytes'; -import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; import { Context, Format, Meta, UrlResult } from '../types'; import { extractUrlFromPacked, guessHeightFromPlaylist } from '../utils'; @@ -17,7 +15,9 @@ export class SuperVideo extends Extractor { } public override normalize(url: URL): URL { - return new URL(url.href.replace('/e/', '/').replace('/k/', '/').replace('/embed-', '/')); + const fileUrl = new URL(url.href.replace('/e/', '/').replace('/k/', '/').replace('/embed-', '/').replace('.html', '')); + + return new URL(`/e${fileUrl.pathname}`, fileUrl.origin); } protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { @@ -25,25 +25,12 @@ export class SuperVideo extends Extractor { const html = await this.fetcher.text(ctx, url, { headers }); - if (html.includes('This video can be watched as embed only')) { - return await this.extractInternal(ctx, new URL(`/e${url.pathname}`, url.origin), meta); - } - - if (/'The file was deleted|The file expired|Video is processing/.test(html)) { + if (/'The file was deleted|The file expired|Video is processing/.test(html) || !html.includes('p,a,c,k,e,d')) { throw new NotFoundError(); } const m3u8Url = extractUrlFromPacked(html, [/sources:\[{file:"(.*?)"/]); - const heightAndSizeMatch = html.match(/\d{3,}x(\d{3,}), ([\d.]+ ?[GM]B)/); - const size = heightAndSizeMatch ? bytes.parse(heightAndSizeMatch[2] as string) as number : undefined; - const height = heightAndSizeMatch - ? parseInt(heightAndSizeMatch[1] as string) - : await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers }); - - const $ = cheerio.load(html); - const title = $('.download__title').text().trim(); - return [ { url: m3u8Url, @@ -53,9 +40,7 @@ export class SuperVideo extends Extractor { ttl: this.ttl, meta: { ...meta, - title, - ...(size && { bytes: size }), - ...(height && { height }), + height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers }), }, }, ]; diff --git a/src/extractor/__fixtures__/SuperVideo/https:hfs307.serversicuro.cchlsdnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq.urlsetmaster.m3u8 b/src/extractor/__fixtures__/SuperVideo/https:hfs307.serversicuro.cchlsdnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq.urlsetmaster.m3u8 new file mode 100644 index 0000000..0d3125d --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:hfs307.serversicuro.cchlsdnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq.urlsetmaster.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=691379,RESOLUTION=624x344,FRAME-RATE=25.000,CODECS="avc1.64001e,mp4a.40.2" +https://hfs307.serversicuro.cc/hls/dnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq/index-v1-a1.m3u8 + +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=39699,RESOLUTION=624x344,CODECS="avc1.64001e",URI="https://hfs307.serversicuro.cc/hls/dnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq/iframes-v1-a1.m3u8" diff --git a/src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna.urlsetmaster.m3u8 b/src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq.urlsetmaster.m3u8 similarity index 74% rename from src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna.urlsetmaster.m3u8 rename to src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq.urlsetmaster.m3u8 index 5c5be2c..420eb28 100644 --- a/src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna.urlsetmaster.m3u8 +++ b/src/extractor/__fixtures__/SuperVideo/https:hfs308.serversicuro.cchlsdnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq.urlsetmaster.m3u8 @@ -1,5 +1,5 @@ #EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3056778,RESOLUTION=1920x1080,FRAME-RATE=25.000,CODECS="avc1.640828,mp4a.40.2" -https://hfs308.serversicuro.cc/hls/dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna/index-v1-a1.m3u8 +https://hfs308.serversicuro.cc/hls/dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq/index-v1-a1.m3u8 -#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=215307,RESOLUTION=1920x1080,CODECS="avc1.640828",URI="https://hfs308.serversicuro.cc/hls/dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna/iframes-v1-a1.m3u8" +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=215307,RESOLUTION=1920x1080,CODECS="avc1.640828",URI="https://hfs308.serversicuro.cc/hls/dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq/iframes-v1-a1.m3u8" diff --git a/src/extractor/__fixtures__/SuperVideo/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa.urlsetmaster.m3u8 b/src/extractor/__fixtures__/SuperVideo/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa.urlsetmaster.m3u8 new file mode 100644 index 0000000..06df04b --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa.urlsetmaster.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1339859,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.64001f,mp4a.40.2" +https://hfs309.serversicuro.cc/hls/dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa/index-v1-a1.m3u8 + +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86074,RESOLUTION=1280x720,CODECS="avc1.64001f",URI="https://hfs309.serversicuro.cc/hls/dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa/iframes-v1-a1.m3u8" diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.cc3h1qqoqtldo8 b/src/extractor/__fixtures__/SuperVideo/https:supervideo.cc3h1qqoqtldo8 deleted file mode 100644 index be41446..0000000 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.cc3h1qqoqtldo8 +++ /dev/null @@ -1,381 +0,0 @@ - - - - - - - Watch Battle Royale (2000) - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - - -
Video is processing now.
Conversion stage: ...
- - - - - - -
-
- -
-
- -
-
-

- Battle Royale (2000) -

-
    -
  • - on - Jul 24, 2022 -
  • -
  • - germancloud123 -
  • -
  • - 373 views -
  • -
  • - -
  • - -
-
-
-
    -
  • -
  • -
-
-
-
- - - -
-
-
- -
-
-
-
-
- -
-
-
- -
- -
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccbj6szat1pval b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccbj6szat1pval deleted file mode 100644 index 0261fd0..0000000 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccbj6szat1pval +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - Watch the walking dead dead city s02e05 german 1080p web x264 wvf - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - -
- - -
- This video can be watched as embed only -
- -
- - - - - - - - \ No newline at end of file diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.cce3h1qqoqtldo8 b/src/extractor/__fixtures__/SuperVideo/https:supervideo.cce3h1qqoqtldo8 new file mode 100644 index 0000000..4196690 --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.cce3h1qqoqtldo8 @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
Video is processing now.
Conversion stage: ...
+ +
+ + + + + + + + + + diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccebj6szat1pval b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccebj6szat1pval index e9d3456..c2050fc 100644 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccebj6szat1pval +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccebj6szat1pval @@ -3,10 +3,10 @@ - - - - + + + - + + + + + + + + + + + - - - - - - - -
-
-
Disable ADBlock plugin and allow pop-ups in your browser to watch video
-
-
- Upgrade you account to watch videos with no limits! -
- - - - + + + +
- - - + diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccendf5shmy9lpt b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccendf5shmy9lpt new file mode 100644 index 0000000..c99bce7 --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccendf5shmy9lpt @@ -0,0 +1,468 @@ + + + + + + + + + +
+ + + + + + +
+
+0:00 + + + +95:12 +
+
+ + + + + + + + + + +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + + + + diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.cceq7i0sw1oytw3 b/src/extractor/__fixtures__/SuperVideo/https:supervideo.cceq7i0sw1oytw3 new file mode 100644 index 0000000..2174f60 --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.cceq7i0sw1oytw3 @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccndf5shmy9lpt b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccndf5shmy9lpt deleted file mode 100644 index 6b3ff50..0000000 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccndf5shmy9lpt +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - Watch - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - -
-
-
-

- File Not Found -

-

The file you were looking for could not be found, sorry for any inconvenience.

-

Possible causes of this error could be:

-
    -
  • - The file expired -
  • - The file was deleted by its owner -
  • - The file was deleted by administration because it didn't comply with our Terms of Use -
-
-
-
- - - - - \ No newline at end of file diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccq7i0sw1oytw3 b/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccq7i0sw1oytw3 deleted file mode 100644 index ca34d41..0000000 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.ccq7i0sw1oytw3 +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - Watch des teufels bad 2024 - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - - - - - - -
-
- - - - - - - - - - - - - - - -
-
-
- -
-
-

- des-teufels-bad-2024 -

-
    -
  • - on - Jun 27, 2024 -
  • -
  • - germancloud123 -
  • -
  • - 1868 views -
  • -
  • - -
  • - -
-
-
-
    -
  • -
  • -
-
-
-
- - - -
-
-
- -
-
-
-
-
- -
-
-
- -
- -
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.tv1p0m1fi9mok8.html b/src/extractor/__fixtures__/SuperVideo/https:supervideo.tv1p0m1fi9mok8.html deleted file mode 100644 index 28a2af1..0000000 --- a/src/extractor/__fixtures__/SuperVideo/https:supervideo.tv1p0m1fi9mok8.html +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - Watch Babylon 2x3 - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - - - - - - -
-
- - - - - - - - - - - - - - - -
-
-
- -
-
-

- Babylon 5 2x3 -

-
    -
  • - on - May 8, 2025 -
  • -
  • - spainseries -
  • -
  • - 14 views -
  • -
  • - -
  • - -
-
-
-
    -
  • -
  • -
-
-
-
- - - -
-
-
- -
-
-
-
-
- -
-
-
- -
- -
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8 b/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8 new file mode 100644 index 0000000..21bdd4e --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8 @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + diff --git a/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8.html.error b/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8.html.error new file mode 100644 index 0000000..c31051e --- /dev/null +++ b/src/extractor/__fixtures__/SuperVideo/https:supervideo.tve1p0m1fi9mok8.html.error @@ -0,0 +1 @@ +Fetcher error: 404: Not Found \ No newline at end of file diff --git a/src/extractor/__snapshots__/SuperVideo.test.ts.snap b/src/extractor/__snapshots__/SuperVideo.test.ts.snap index 6027b7f..eb43d4d 100644 --- a/src/extractor/__snapshots__/SuperVideo.test.ts.snap +++ b/src/extractor/__snapshots__/SuperVideo.test.ts.snap @@ -10,11 +10,10 @@ exports[`SuperVideo embed only 1`] = ` "meta": { "countryCodes": [], "height": 1080, - "title": "", }, "sourceId": "supervideo_", "ttl": 10800000, - "url": "https://hfs308.serversicuro.cc/hls/,dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna,.urlset/master.m3u8", + "url": "https://hfs308.serversicuro.cc/hls/,dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2lwk3cmu5azdcnsq,.urlset/master.m3u8", }, ] `; @@ -27,14 +26,12 @@ exports[`SuperVideo supervideo.cc /e/ 1`] = ` "format": "hls", "label": "SuperVideo", "meta": { - "bytes": 1073741824, "countryCodes": [], "height": 720, - "title": "des-teufels-bad-2024", }, "sourceId": "supervideo_", "ttl": 10800000, - "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p577fze64cp3rsmdyq,.urlset/master.m3u8", + "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdq45p6s4zxa,.urlset/master.m3u8", }, ] `; @@ -45,14 +42,12 @@ exports[`SuperVideo supervideo.tv /embed-/ 1`] = ` "format": "hls", "label": "SuperVideo", "meta": { - "bytes": 219571814, "countryCodes": [], "height": 344, - "title": "Babylon 5 2x3", }, "sourceId": "supervideo_", "ttl": 10800000, - "url": "https://hfs307.serversicuro.cc/hls/,dnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kmc4ohiklh5qwq4q,.urlset/master.m3u8", + "url": "https://hfs307.serversicuro.cc/hls/,dnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kaehp2e7ro7gd4pq,.urlset/master.m3u8", }, ] `; diff --git a/src/source/Source.ts b/src/source/Source.ts index 2ef1bd6..3f5feec 100644 --- a/src/source/Source.ts +++ b/src/source/Source.ts @@ -4,7 +4,7 @@ import { Cacheable, CacheableMemory, Keyv } from 'cacheable'; import { ContentType } from 'stremio-addon-sdk'; import { NotFoundError } from '../error'; import { Context, CountryCode, Meta } from '../types'; -import { getCacheDir, Id, noCache } from '../utils'; +import { getCacheDir, Id } from '../utils'; export interface SourceResult { url: URL; @@ -44,7 +44,7 @@ export abstract class Source { let sourceResults = (await Source.sourceResultCache.get(cacheKey)) ?.map(sourceResult => ({ ...sourceResult, url: new URL(sourceResult.url) })); - if (!sourceResults || noCache(ctx.config)) { + if (!sourceResults) { try { sourceResults = await this.handleInternal(ctx, type, id); } catch (error) { diff --git a/src/types.ts b/src/types.ts index c04729e..0857a68 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,7 @@ export type CustomManifest = Manifest & { }; }; -export type Config = Partial & Record<`disableExtractor_${string}`, string>>; +export type Config = Partial & Record<`disableExtractor_${string}`, string>>; export enum CountryCode { multi = 'multi', diff --git a/src/utils/Fetcher.ts b/src/utils/Fetcher.ts index 77be3a1..4a96035 100644 --- a/src/utils/Fetcher.ts +++ b/src/utils/Fetcher.ts @@ -7,8 +7,7 @@ import { fetch, Headers, RequestInit, Response } from 'undici'; import winston from 'winston'; import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TimeoutError, TooManyRequestsError, TooManyTimeoutsError } from '../error'; import { BlockedReason, Context } from '../types'; -import { noCache } from './config'; -import { createProxyAgent, getProxyForUrl } from './dispatcher'; +import { createDispatcher } from './dispatcher'; import { envGet } from './env'; interface HttpCacheItem { @@ -234,9 +233,9 @@ export class Fetcher { const cacheKey = this.determineCacheKey(url, init); let httpCacheItem = await this.cacheGet(cacheKey); - const disableCache = init?.noCache ?? noCache(ctx.config); - if (httpCacheItem && !disableCache) { - this.logger.info(`Cached fetch ${request.method} ${url}: ${httpCacheItem.status} (${httpCacheItem.statusText})`, ctx); + const noCache = init?.noCache ?? false; + if (httpCacheItem && !noCache) { + this.logger.info(`Cached fetch ${request.method} ${url}`, ctx); return this.handleHttpCacheItem(ctx, httpCacheItem, url, init); } @@ -254,16 +253,14 @@ export class Fetcher { }; protected async fetchWithTimeout(ctx: Context, url: URL, init?: CustomRequestInit, tryCount = 0): Promise { - const proxyUrl = getProxyForUrl(ctx, url); - - this.logger.info(`Fetch ${init?.method ?? 'GET'} ${url} via proxy ${proxyUrl}`, ctx); + this.logger.info(`Fetch ${init?.method ?? 'GET'} ${url}`, ctx); const isRateLimitedRaw = await this.rateLimitedCache.getRaw(url.host); /* istanbul ignore if */ if (isRateLimitedRaw && isRateLimitedRaw.value && isRateLimitedRaw.expires) { const ttl = isRateLimitedRaw.expires - Date.now(); if (ttl <= this.MAX_WAIT_RETRY_AFTER && tryCount < 1) { - this.logger.info(`Wait out rate limit for ${url}`, ctx); + this.logger.info('Wait out rate limit', ctx); await this.sleep(ttl); @@ -284,17 +281,17 @@ export class Fetcher { finalUrl.username = ''; finalUrl.password = ''; + const dispatcher = createDispatcher(ctx, url); + const finalInit = { ...init, keepalive: true, signal: AbortSignal.timeout(init?.timeout ?? this.DEFAULT_TIMEOUT), - ...(/* istanbul ignore next */ proxyUrl && { dispatcher: createProxyAgent(proxyUrl) }), + ...(/* istanbul ignore next */ dispatcher && { dispatcher }), }; response = await fetch(finalUrl, finalInit); } catch (error) { - this.logger.info(`Got error ${error} for ${url}`, ctx); - if (error instanceof DOMException && ['AbortError', 'TimeoutError'].includes(error.name)) { await this.increaseTimeoutsCount(url); throw new TimeoutError(); @@ -303,8 +300,6 @@ export class Fetcher { throw error; } - this.logger.info(`Got ${response.status} (${response.statusText}) for ${url}`, ctx); - await this.decreaseTimeoutsCount(url); if (response.status === 429) { diff --git a/src/utils/StreamResolver.ts b/src/utils/StreamResolver.ts index 5b78976..b19fba3 100644 --- a/src/utils/StreamResolver.ts +++ b/src/utils/StreamResolver.ts @@ -39,7 +39,7 @@ export class StreamResolver { const streams: Stream[] = []; - let sourceErrorCount = 0; + let sourceErrorOccurred = false; const urlResults: UrlResult[] = []; const sourcePromises = sources.map(async (source) => { if (!source.contentTypes.includes(type)) { @@ -55,7 +55,7 @@ export class StreamResolver { urlResults.push(...sourceUrlResults.flat()); } catch (error) { - sourceErrorCount++; + sourceErrorOccurred = true; if (showErrors(ctx.config)) { streams.push({ @@ -86,8 +86,7 @@ export class StreamResolver { return a.label.localeCompare(b.label); }); - const errorCount = urlResults.reduce((count, urlResult) => urlResult.error ? count + 1 : count, sourceErrorCount); - this.logger.info(`Got ${urlResults.length} url results, including ${errorCount} errors`, ctx); + this.logger.info(`Return ${urlResults.length} streams`, ctx); streams.push( ...urlResults.filter(urlResult => !urlResult.error || showErrors(ctx.config)) @@ -107,7 +106,7 @@ export class StreamResolver { })), ); - const ttl = sourceErrorCount === 0 ? this.determineTtl(urlResults) : undefined; + const ttl = !sourceErrorOccurred ? this.determineTtl(urlResults) : undefined; return { streams, diff --git a/src/utils/__fixtures__/StreamResolver/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa.urlsetmaster.m3u8 b/src/utils/__fixtures__/StreamResolver/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa.urlsetmaster.m3u8 new file mode 100644 index 0000000..10e0f94 --- /dev/null +++ b/src/utils/__fixtures__/StreamResolver/https:hfs309.serversicuro.cchlsdnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa.urlsetmaster.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1339859,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.64001f,mp4a.40.2" +https://hfs309.serversicuro.cc/hls/dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa/index-v1-a1.m3u8 + +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86074,RESOLUTION=1280x720,CODECS="avc1.64001f",URI="https://hfs309.serversicuro.cc/hls/dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa/iframes-v1-a1.m3u8" diff --git a/src/utils/__fixtures__/StreamResolver/https:hfs311.serversicuro.cchlsdnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq.urlsetmaster.m3u8 b/src/utils/__fixtures__/StreamResolver/https:hfs311.serversicuro.cchlsdnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq.urlsetmaster.m3u8 new file mode 100644 index 0000000..11a2c02 --- /dev/null +++ b/src/utils/__fixtures__/StreamResolver/https:hfs311.serversicuro.cchlsdnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq.urlsetmaster.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1315290,RESOLUTION=1280x720,FRAME-RATE=23.974,CODECS="avc1.4d401f,mp4a.40.2" +https://hfs311.serversicuro.cc/hls/dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq/index-v1-a1.m3u8 + +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=28034,RESOLUTION=1280x720,CODECS="avc1.4d401f",URI="https://hfs311.serversicuro.cc/hls/dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq/iframes-v1-a1.m3u8" diff --git a/src/utils/__fixtures__/StreamResolver/https:supervideo.cc6vri8jyhla3n b/src/utils/__fixtures__/StreamResolver/https:supervideo.cc6vri8jyhla3n deleted file mode 100644 index d6eaead..0000000 --- a/src/utils/__fixtures__/StreamResolver/https:supervideo.cc6vri8jyhla3n +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - Watch the devils bath 2024 sub ita - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - - - - - - -
-
- - - - - - - - - - - - - - - -
-
-
- -
-
-

- the-devils-bath-2024-sd-sub-ita -

-
    -
  • - on - Jul 7, 2024 -
  • -
  • - fracobello -
  • -
  • - 2549 views -
  • -
  • - -
  • - -
-
-
-
    -
  • -
  • -
-
-
-
- - - -
-
-
- -
-
-
-
-
- -
-
-
- -
- -
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/utils/__fixtures__/StreamResolver/https:supervideo.cce6vri8jyhla3n b/src/utils/__fixtures__/StreamResolver/https:supervideo.cce6vri8jyhla3n new file mode 100644 index 0000000..c831bf2 --- /dev/null +++ b/src/utils/__fixtures__/StreamResolver/https:supervideo.cce6vri8jyhla3n @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + diff --git a/src/utils/__fixtures__/StreamResolver/https:supervideo.cceq7i0sw1oytw3 b/src/utils/__fixtures__/StreamResolver/https:supervideo.cceq7i0sw1oytw3 new file mode 100644 index 0000000..4e2d0e2 --- /dev/null +++ b/src/utils/__fixtures__/StreamResolver/https:supervideo.cceq7i0sw1oytw3 @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + diff --git a/src/utils/__fixtures__/StreamResolver/https:supervideo.ccq7i0sw1oytw3 b/src/utils/__fixtures__/StreamResolver/https:supervideo.ccq7i0sw1oytw3 deleted file mode 100644 index ef0698b..0000000 --- a/src/utils/__fixtures__/StreamResolver/https:supervideo.ccq7i0sw1oytw3 +++ /dev/null @@ -1,406 +0,0 @@ - - - - - - - Watch des teufels bad 2024 - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
- - - - - - - -
-
- - - - - - - - - - - - - - - -
-
-
- -
-
-

- des-teufels-bad-2024 -

-
    -
  • - on - Jun 27, 2024 -
  • -
  • - germancloud123 -
  • -
  • - 1868 views -
  • -
  • - -
  • - -
-
-
-
    -
  • -
  • -
-
-
-
- - - -
-
-
- -
-
-
-
-
- -
-
-
- -
- -
-
- - -
-
-
-
- - -
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/src/utils/__snapshots__/StreamResolver.test.ts.snap b/src/utils/__snapshots__/StreamResolver.test.ts.snap index c097f06..6c4d2ff 100644 --- a/src/utils/__snapshots__/StreamResolver.test.ts.snap +++ b/src/utils/__snapshots__/StreamResolver.test.ts.snap @@ -208,25 +208,21 @@ exports[`resolve returns sorted results 1`] = ` }, { "behaviorHints": { - "bingeGroup": "webstreamr-supervideo_it", + "bingeGroup": "webstreamr-supervideo_de", "notWebReady": true, - "videoSize": 1181116006, }, - "name": "WebStreamr 🇮🇹 720p", - "title": "the-devils-bath-2024-sd-sub-ita -💾 1.1 GB 🔗 SuperVideo", - "url": "https://hfs311.serversicuro.cc/hls/,dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpzb6josghbkwhnisq,.urlset/master.m3u8", + "name": "WebStreamr 🇩🇪 720p", + "title": "🔗 SuperVideo", + "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa,.urlset/master.m3u8", }, { "behaviorHints": { - "bingeGroup": "webstreamr-supervideo_de", + "bingeGroup": "webstreamr-supervideo_it", "notWebReady": true, - "videoSize": 1073741824, }, - "name": "WebStreamr 🇩🇪 720p", - "title": "des-teufels-bad-2024 -💾 1 GB 🔗 SuperVideo", - "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p577fze64mz3bsmdyq,.urlset/master.m3u8", + "name": "WebStreamr 🇮🇹 720p", + "title": "🔗 SuperVideo", + "url": "https://hfs311.serversicuro.cc/hls/,dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq,.urlset/master.m3u8", }, { "behaviorHints": { @@ -288,25 +284,21 @@ exports[`resolve returns sorted results 2`] = ` }, { "behaviorHints": { - "bingeGroup": "webstreamr-supervideo_it", + "bingeGroup": "webstreamr-supervideo_de", "notWebReady": true, - "videoSize": 1181116006, }, - "name": "WebStreamr 🇮🇹 720p", - "title": "the-devils-bath-2024-sd-sub-ita -💾 1.1 GB 🔗 SuperVideo", - "url": "https://hfs311.serversicuro.cc/hls/,dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpzb6josghbkwhnisq,.urlset/master.m3u8", + "name": "WebStreamr 🇩🇪 720p", + "title": "🔗 SuperVideo", + "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p57tdcfdqtnows4zxa,.urlset/master.m3u8", }, { "behaviorHints": { - "bingeGroup": "webstreamr-supervideo_de", + "bingeGroup": "webstreamr-supervideo_it", "notWebReady": true, - "videoSize": 1073741824, }, - "name": "WebStreamr 🇩🇪 720p", - "title": "des-teufels-bad-2024 -💾 1 GB 🔗 SuperVideo", - "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p577fze64mz3bsmdyq,.urlset/master.m3u8", + "name": "WebStreamr 🇮🇹 720p", + "title": "🔗 SuperVideo", + "url": "https://hfs311.serversicuro.cc/hls/,dnzpcjj427g4a3gyvbth53rxqgffbu4vol4o7gg7pskpznysppkxbh6kuuxq,.urlset/master.m3u8", }, { "behaviorHints": { diff --git a/src/utils/__snapshots__/manifest.test.ts.snap b/src/utils/__snapshots__/manifest.test.ts.snap index e1a58a7..452b4fb 100644 --- a/src/utils/__snapshots__/manifest.test.ts.snap +++ b/src/utils/__snapshots__/manifest.test.ts.snap @@ -72,11 +72,6 @@ exports[`buildManifest default manifest 1`] = ` "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, ], "description": "Provides HTTP URLs from streaming websites. Configure add-on for additional languages. Add MediaFlow proxy for more URLs. @@ -136,11 +131,6 @@ exports[`buildManifest disable extractors 1`] = ` "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, { "default": "checked", "key": "disableExtractor_doodstream", @@ -155,9 +145,10 @@ exports[`buildManifest disable extractors 1`] = ` ] `; -exports[`buildManifest has checked includeExternalUrls 1`] = ` +exports[`buildManifest has checked showErrors and includeExternalUrls 1`] = ` [ { + "default": "checked", "key": "showErrors", "title": "Show errors", "type": "checkbox", @@ -186,89 +177,6 @@ exports[`buildManifest has checked includeExternalUrls 1`] = ` "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, -] -`; - -exports[`buildManifest has checked noCache 1`] = ` -[ - { - "key": "showErrors", - "title": "Show errors", - "type": "checkbox", - }, - { - "key": "includeExternalUrls", - "title": "Include external URLs in results", - "type": "checkbox", - }, - { - "default": "", - "key": "mediaFlowProxyUrl", - "title": "MediaFlow Proxy URL", - "type": "text", - }, - { - "default": "", - "key": "mediaFlowProxyPassword", - "title": "MediaFlow Proxy Password", - "type": "password", - }, - { - "default": "", - "key": "proxyConfig", - "title": "Proxy Config", - "type": "text", - }, - { - "default": "checked", - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, -] -`; - -exports[`buildManifest has checked showErrors 1`] = ` -[ - { - "default": "checked", - "key": "showErrors", - "title": "Show errors", - "type": "checkbox", - }, - { - "key": "includeExternalUrls", - "title": "Include external URLs in results", - "type": "checkbox", - }, - { - "default": "", - "key": "mediaFlowProxyUrl", - "title": "MediaFlow Proxy URL", - "type": "text", - }, - { - "default": "", - "key": "mediaFlowProxyPassword", - "title": "MediaFlow Proxy Password", - "type": "password", - }, - { - "default": "", - "key": "proxyConfig", - "title": "Proxy Config", - "type": "text", - }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, ] `; @@ -319,11 +227,6 @@ exports[`buildManifest has checked source with appropriate config 1`] = ` "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, ] `; @@ -382,11 +285,6 @@ exports[`buildManifest has unchecked source without a config 1`] = ` "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, ] `; @@ -420,10 +318,5 @@ exports[`buildManifest showErrors and includeExternalUrls are unchecked by defau "title": "Proxy Config", "type": "text", }, - { - "key": "noCache", - "title": "Disable cache", - "type": "checkbox", - }, ] `; diff --git a/src/utils/config.ts b/src/utils/config.ts index 2702554..fd503db 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -9,8 +9,6 @@ export const showErrors = (config: Config): boolean => 'showErrors' in config; export const showExternalUrls = (config: Config): boolean => 'includeExternalUrls' in config; -export const noCache = (config: Config): boolean => 'noCache' in config; - export const hasMultiEnabled = (config: Config): boolean => 'multi' in config; export const disableExtractorConfigKey = (extractor: Extractor): string => `disableExtractor_${extractor.id}`; diff --git a/src/utils/dispatcher.ts b/src/utils/dispatcher.ts index 8e05c77..e15b340 100644 --- a/src/utils/dispatcher.ts +++ b/src/utils/dispatcher.ts @@ -3,7 +3,15 @@ import { minimatch } from 'minimatch'; import { Dispatcher, ProxyAgent } from 'undici'; import { Context } from '../types'; -export const getProxyForUrl = (ctx: Context, url: URL): URL | undefined => { +const createProxyAgent = (proxyUrl: URL): Dispatcher => { + if (proxyUrl.protocol === 'socks5:') { + return socksDispatcher({ type: 5, host: proxyUrl.hostname, port: parseInt(proxyUrl.port) }); + } + + return new ProxyAgent({ uri: proxyUrl.href }); +}; + +const createBasicDispatcher = (ctx: Context, url: URL): Dispatcher | undefined => { const proxyConfig = ctx.config['proxyConfig'] || process.env['PROXY_CONFIG']; if (proxyConfig) { @@ -14,20 +22,16 @@ export const getProxyForUrl = (ctx: Context, url: URL): URL | undefined => { } if (hostPattern === '*' || minimatch(url.host, hostPattern)) { - return proxy === 'false' ? undefined : new URL(proxy); + return createProxyAgent(new URL(proxy)); } } } else if (process.env['ALL_PROXY']) { - return new URL(process.env['ALL_PROXY']); + return createProxyAgent(new URL(process.env['ALL_PROXY'])); } return undefined; }; -export const createProxyAgent = (proxyUrl: URL): Dispatcher => { - if (proxyUrl.protocol === 'socks5:') { - return socksDispatcher({ type: 5, host: proxyUrl.hostname, port: parseInt(proxyUrl.port) }); - } - - return new ProxyAgent({ uri: proxyUrl.href }); +export const createDispatcher = (ctx: Context, url: URL): Dispatcher | undefined => { + return createBasicDispatcher(ctx, url); }; diff --git a/src/utils/manifest.test.ts b/src/utils/manifest.test.ts index 9e71309..bbe709d 100644 --- a/src/utils/manifest.test.ts +++ b/src/utils/manifest.test.ts @@ -50,20 +50,8 @@ describe('buildManifest', () => { expect(manifest.config).toMatchSnapshot(); }); - test('has checked showErrors', () => { - const manifest = buildManifest([], [], { showErrors: 'on' }); - - expect(manifest.config).toMatchSnapshot(); - }); - - test('has checked includeExternalUrls', () => { - const manifest = buildManifest([], [], { includeExternalUrls: 'on' }); - - expect(manifest.config).toMatchSnapshot(); - }); - - test('has checked noCache', () => { - const manifest = buildManifest([], [], { noCache: 'on' }); + test('has checked showErrors and includeExternalUrls', () => { + const manifest = buildManifest([], [], { showErrors: 'on', includeExternalUrls: 'on' }); expect(manifest.config).toMatchSnapshot(); }); diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index 2b9e238..e954fc5 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -10,7 +10,7 @@ const typedEntries = (obj: T): [keyof T, T[keyof T]][] => (Obj export const buildManifest = (sources: Source[], extractors: Extractor[], config: Config): CustomManifest => { const manifest: CustomManifest = { id: envGetAppId(), - version: '0.51.0', // x-release-please-version + version: '0.50.4', // x-release-please-version name: envGetAppName(), description: 'Provides HTTP URLs from streaming websites. Configure add-on for additional languages. Add MediaFlow proxy for more URLs.', resources: [ @@ -96,13 +96,6 @@ export const buildManifest = (sources: Source[], extractors: Extractor[], config default: config['proxyConfig'] ?? '', }); - manifest.config.push({ - key: 'noCache', - type: 'checkbox', - title: 'Disable cache', - ...('noCache' in config && { default: 'checked' }), - }); - extractors.forEach((extractor) => { if (extractor.id === 'external') { return;