diff --git a/src/types.ts b/src/types.ts index c9f9f3d..eb3e19f 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> & Record<`excludeResolution_${string}`, string>>; export enum CountryCode { multi = 'multi', diff --git a/src/utils/StreamResolver.ts b/src/utils/StreamResolver.ts index 0023a6b..ff641d1 100644 --- a/src/utils/StreamResolver.ts +++ b/src/utils/StreamResolver.ts @@ -6,10 +6,11 @@ import { logErrorAndReturnNiceString } from '../error'; import { ExtractorRegistry } from '../extractor'; import { Source } from '../source'; import { Context, CountryCode, Format, UrlResult } from '../types'; -import { showErrors, showExternalUrls } from './config'; +import { isResolutionExcluded, showErrors, showExternalUrls } from './config'; import { envGetAppName } from './env'; import { Id } from './id'; import { flagFromCountryCode } from './language'; +import { getClosestResolution } from './resolution'; interface ResolveResponse { streams: Stream[]; @@ -142,7 +143,7 @@ export class StreamResolver { this.logger.info(`Got ${urlResults.length} url results, including ${errorCount} errors`, ctx); streams.push( - ...urlResults.filter(urlResult => !urlResult.error || showErrors(ctx.config)) + ...urlResults.filter(urlResult => (!urlResult.error || showErrors(ctx.config)) && !isResolutionExcluded(ctx.config, getClosestResolution(urlResult.meta?.height))) .map(urlResult => ({ ...this.buildUrl(urlResult), name: this.buildName(ctx, urlResult), @@ -200,7 +201,7 @@ export class StreamResolver { }); if (urlResult.meta?.height) { - name += ` ${urlResult.meta.height}p`; + name += ` ${getClosestResolution(urlResult.meta.height)}`; } if (urlResult.isExternal && showExternalUrls(ctx.config)) { diff --git a/src/utils/__snapshots__/manifest.test.ts.snap b/src/utils/__snapshots__/manifest.test.ts.snap index 0a31055..3282343 100644 --- a/src/utils/__snapshots__/manifest.test.ts.snap +++ b/src/utils/__snapshots__/manifest.test.ts.snap @@ -81,6 +81,56 @@ exports[`buildManifest default manifest 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ], "description": "Provides HTTP URLs from streaming websites. Configure add-on for additional languages. Add MediaFlow proxy for more URLs. @@ -135,6 +185,56 @@ exports[`buildManifest disable extractors 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, { "default": "checked", "key": "disableExtractor_doodstream", @@ -149,6 +249,84 @@ exports[`buildManifest disable extractors 1`] = ` ] `; +exports[`buildManifest has checked excludeResolution_2160p 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": "checked", + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, +] +`; + exports[`buildManifest has checked includeExternalUrls 1`] = ` [ { @@ -174,6 +352,56 @@ exports[`buildManifest has checked includeExternalUrls 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ] `; @@ -202,6 +430,56 @@ exports[`buildManifest has checked showErrors 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ] `; @@ -246,6 +524,56 @@ exports[`buildManifest has checked source with appropriate config 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ] `; @@ -298,6 +626,56 @@ exports[`buildManifest has unchecked source without a config 1`] = ` "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ] `; @@ -325,5 +703,55 @@ exports[`buildManifest showErrors and includeExternalUrls are unchecked by defau "title": "MediaFlow Proxy Password", "type": "password", }, + { + "key": "excludeResolution_2160p", + "title": "Exclude resolution 2160p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1440p", + "title": "Exclude resolution 1440p", + "type": "checkbox", + }, + { + "key": "excludeResolution_1080p", + "title": "Exclude resolution 1080p", + "type": "checkbox", + }, + { + "key": "excludeResolution_720p", + "title": "Exclude resolution 720p", + "type": "checkbox", + }, + { + "key": "excludeResolution_576p", + "title": "Exclude resolution 576p", + "type": "checkbox", + }, + { + "key": "excludeResolution_480p", + "title": "Exclude resolution 480p", + "type": "checkbox", + }, + { + "key": "excludeResolution_360p", + "title": "Exclude resolution 360p", + "type": "checkbox", + }, + { + "key": "excludeResolution_240p", + "title": "Exclude resolution 240p", + "type": "checkbox", + }, + { + "key": "excludeResolution_144p", + "title": "Exclude resolution 144p", + "type": "checkbox", + }, + { + "key": "excludeResolution_Unknown", + "title": "Exclude resolution Unknown", + "type": "checkbox", + }, ] `; diff --git a/src/utils/config.ts b/src/utils/config.ts index fd503db..4661083 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -14,3 +14,7 @@ export const hasMultiEnabled = (config: Config): boolean => 'multi' in config; export const disableExtractorConfigKey = (extractor: Extractor): string => `disableExtractor_${extractor.id}`; export const isExtractorDisabled = (config: Config, extractor: Extractor): boolean => disableExtractorConfigKey(extractor) in config; + +export const excludeResolutionConfigKey = (resolution: string): string => `excludeResolution_${resolution}`; + +export const isResolutionExcluded = (config: Config, resolution: string): boolean => excludeResolutionConfigKey(resolution) in config; diff --git a/src/utils/index.ts b/src/utils/index.ts index 35dec8b..13a26bd 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,4 +11,5 @@ export * from './id'; export * from './language'; export * from './manifest'; export * from './media-flow-proxy'; +export * from './resolution'; export * from './tmdb'; diff --git a/src/utils/manifest.test.ts b/src/utils/manifest.test.ts index 7f6f236..b1015fa 100644 --- a/src/utils/manifest.test.ts +++ b/src/utils/manifest.test.ts @@ -72,4 +72,10 @@ describe('buildManifest', () => { expect(manifest.config).toMatchSnapshot(); }); + + test('has checked excludeResolution_2160p', () => { + const manifest = buildManifest([], [], { excludeResolution_2160p: 'on' }); + + expect(manifest.config).toMatchSnapshot(); + }); }); diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index 81a7224..73010db 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -1,9 +1,15 @@ import { Extractor } from '../extractor'; import { Source } from '../source'; import { Config, CountryCode, CustomManifest } from '../types'; -import { disableExtractorConfigKey, isExtractorDisabled } from './config'; +import { + disableExtractorConfigKey, + excludeResolutionConfigKey, + isExtractorDisabled, + isResolutionExcluded, +} from './config'; import { envGetAppId, envGetAppName } from './env'; import { flagFromCountryCode, languageFromCountryCode } from './language'; +import { RESOLUTIONS } from './resolution'; const typedEntries = (obj: T): [keyof T, T[keyof T]][] => (Object.entries(obj) as [keyof T, T[keyof T]][]); @@ -92,6 +98,15 @@ export const buildManifest = (sources: Source[], extractors: Extractor[], config default: config['mediaFlowProxyPassword'] ?? '', }); + RESOLUTIONS.forEach((resolution) => { + manifest.config.push({ + key: excludeResolutionConfigKey(resolution), + type: 'checkbox', + title: `Exclude resolution ${resolution}`, + ...(isResolutionExcluded(config, resolution) && { default: 'checked' }), + }); + }); + extractors.forEach((extractor) => { if (extractor.id === 'external') { return; diff --git a/src/utils/resolution.ts b/src/utils/resolution.ts new file mode 100644 index 0000000..7323b58 --- /dev/null +++ b/src/utils/resolution.ts @@ -0,0 +1,24 @@ +export const RESOLUTIONS = [ + '2160p', + '1440p', + '1080p', + '720p', + '576p', + '480p', + '360p', + '240p', + '144p', + 'Unknown', +] as const; + +export const getClosestResolution = (height: number | undefined) => { + if (!height) { + return 'Unknown'; + } + + return `${RESOLUTIONS.map(r => Number(r.replace('p', ''))) + .filter(n => !isNaN(n)) + .reduce((prev, curr) => { + return Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev; + })}p`; +};