diff --git a/src/extractor/ExtractorRegistry.ts b/src/extractor/ExtractorRegistry.ts index b65bc83..96a45ad 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 } from '../utils'; +import { getCacheDir, isExtractorDisabled, noCache } 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) { + if (storedDataRaw && expires && !noCache(ctx.config)) { // 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/source/Source.ts b/src/source/Source.ts index 3f5feec..2ef1bd6 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 } from '../utils'; +import { getCacheDir, Id, noCache } 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) { + if (!sourceResults || noCache(ctx.config)) { try { sourceResults = await this.handleInternal(ctx, type, id); } catch (error) { diff --git a/src/types.ts b/src/types.ts index 0857a68..c04729e 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 9731d44..eb5e9c0 100644 --- a/src/utils/Fetcher.ts +++ b/src/utils/Fetcher.ts @@ -7,6 +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 { createDispatcher } from './dispatcher'; import { envGet } from './env'; @@ -233,8 +234,8 @@ export class Fetcher { const cacheKey = this.determineCacheKey(url, init); let httpCacheItem = await this.cacheGet(cacheKey); - const noCache = init?.noCache ?? false; - if (httpCacheItem && !noCache) { + const disableCache = init?.noCache ?? noCache(ctx.config); + if (httpCacheItem && !disableCache) { this.logger.info(`Cached fetch ${request.method} ${url}: ${httpCacheItem.status} (${httpCacheItem.statusText})`, ctx); return this.handleHttpCacheItem(ctx, httpCacheItem, url, init); } diff --git a/src/utils/__snapshots__/manifest.test.ts.snap b/src/utils/__snapshots__/manifest.test.ts.snap index 452b4fb..e1a58a7 100644 --- a/src/utils/__snapshots__/manifest.test.ts.snap +++ b/src/utils/__snapshots__/manifest.test.ts.snap @@ -72,6 +72,11 @@ 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. @@ -131,6 +136,11 @@ exports[`buildManifest disable extractors 1`] = ` "title": "Proxy Config", "type": "text", }, + { + "key": "noCache", + "title": "Disable cache", + "type": "checkbox", + }, { "default": "checked", "key": "disableExtractor_doodstream", @@ -145,10 +155,9 @@ exports[`buildManifest disable extractors 1`] = ` ] `; -exports[`buildManifest has checked showErrors and includeExternalUrls 1`] = ` +exports[`buildManifest has checked includeExternalUrls 1`] = ` [ { - "default": "checked", "key": "showErrors", "title": "Show errors", "type": "checkbox", @@ -177,6 +186,89 @@ exports[`buildManifest has checked showErrors and 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", + }, ] `; @@ -227,6 +319,11 @@ exports[`buildManifest has checked source with appropriate config 1`] = ` "title": "Proxy Config", "type": "text", }, + { + "key": "noCache", + "title": "Disable cache", + "type": "checkbox", + }, ] `; @@ -285,6 +382,11 @@ exports[`buildManifest has unchecked source without a config 1`] = ` "title": "Proxy Config", "type": "text", }, + { + "key": "noCache", + "title": "Disable cache", + "type": "checkbox", + }, ] `; @@ -318,5 +420,10 @@ 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 fd503db..2702554 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -9,6 +9,8 @@ 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/manifest.test.ts b/src/utils/manifest.test.ts index bbe709d..9e71309 100644 --- a/src/utils/manifest.test.ts +++ b/src/utils/manifest.test.ts @@ -50,8 +50,20 @@ describe('buildManifest', () => { expect(manifest.config).toMatchSnapshot(); }); - test('has checked showErrors and includeExternalUrls', () => { - const manifest = buildManifest([], [], { showErrors: 'on', includeExternalUrls: 'on' }); + 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' }); expect(manifest.config).toMatchSnapshot(); }); diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index e954fc5..a1c8d48 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -96,6 +96,13 @@ 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;