From cfcf81b665357afb559afe569ef719f943438d48 Mon Sep 17 00:00:00 2001 From: WebStreamr <210764791+webstreamr@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:55:58 +0000 Subject: [PATCH] refactor: simplify passing around meta --- src/extractor/DoodStream.test.ts | 11 +- src/extractor/DoodStream.ts | 8 +- src/extractor/Dropload.test.ts | 9 +- src/extractor/Dropload.ts | 8 +- src/extractor/ExternalUrl.test.ts | 9 +- src/extractor/ExternalUrl.ts | 11 +- src/extractor/Extractor.ts | 13 +- src/extractor/ExtractorRegistry.test.ts | 22 +- src/extractor/ExtractorRegistry.ts | 6 +- src/extractor/Fastream.test.ts | 3 +- src/extractor/Fastream.ts | 8 +- src/extractor/Fsst.test.ts | 7 +- src/extractor/Fsst.ts | 10 +- src/extractor/KinoGer.test.ts | 5 +- src/extractor/KinoGer.ts | 8 +- src/extractor/Mixdrop.test.ts | 5 +- src/extractor/Mixdrop.ts | 8 +- src/extractor/SaveFiles.test.ts | 11 +- src/extractor/SaveFiles.ts | 8 +- src/extractor/Soaper.test.ts | 7 +- src/extractor/Soaper.ts | 9 +- src/extractor/StreamEmbed.test.ts | 3 +- src/extractor/StreamEmbed.ts | 8 +- src/extractor/Streamtape.test.ts | 3 +- src/extractor/Streamtape.ts | 8 +- src/extractor/SuperVideo.test.ts | 11 +- src/extractor/SuperVideo.ts | 10 +- src/extractor/Uqload.test.ts | 3 +- src/extractor/Uqload.ts | 8 +- src/extractor/VidSrc.test.ts | 9 +- src/extractor/VidSrc.ts | 14 +- src/extractor/VixSrc.test.ts | 7 +- src/extractor/VixSrc.ts | 6 +- src/extractor/XPrime.test.ts | 9 +- src/extractor/XPrime.ts | 8 +- src/extractor/YouTube.test.ts | 5 +- src/extractor/YouTube.ts | 8 +- .../__snapshots__/DoodStream.test.ts.snap | 24 +- .../__snapshots__/Dropload.test.ts.snap | 12 +- .../__snapshots__/ExternalUrl.test.ts.snap | 26 +- .../ExtractorRegistry.test.ts.snap | 33 +- .../__snapshots__/Fastream.test.ts.snap | 6 +- src/extractor/__snapshots__/Fsst.test.ts.snap | 42 +- .../__snapshots__/KinoGer.test.ts.snap | 12 +- .../__snapshots__/Mixdrop.test.ts.snap | 6 +- .../__snapshots__/SaveFiles.test.ts.snap | 24 +- .../__snapshots__/Soaper.test.ts.snap | 21 +- .../__snapshots__/StreamEmbed.test.ts.snap | 6 +- .../__snapshots__/Streamtape.test.ts.snap | 6 +- .../__snapshots__/SuperVideo.test.ts.snap | 18 +- .../__snapshots__/Uqload.test.ts.snap | 6 +- .../__snapshots__/VidSrc.test.ts.snap | 18 +- .../__snapshots__/VixSrc.test.ts.snap | 4 +- .../__snapshots__/XPrime.test.ts.snap | 24 +- .../__snapshots__/YouTube.test.ts.snap | 6 +- src/source/CineHDPlus.ts | 6 +- src/source/Cuevana.ts | 14 +- src/source/Einschalten.ts | 4 +- src/source/Eurostreaming.ts | 2 +- src/source/Frembed.ts | 2 +- src/source/FrenchCloud.ts | 2 +- src/source/HomeCine.ts | 8 +- src/source/KinoGer.ts | 2 +- src/source/MegaKino.ts | 2 +- src/source/MeineCloud.ts | 2 +- src/source/MostraGuarda.ts | 2 +- src/source/Movix.ts | 2 +- src/source/PrimeWire.ts | 2 +- src/source/Soaper.ts | 4 +- src/source/Source.ts | 9 +- src/source/StreamKiste.ts | 4 +- src/source/VerHdLink.ts | 8 +- src/source/VidSrc.ts | 2 +- src/source/VixSrc.ts | 2 +- src/source/XPrime.ts | 4 +- .../__snapshots__/CineHDPlus.test.ts.snap | 36 +- src/source/__snapshots__/Cuevana.test.ts.snap | 336 ++++++++++++---- .../__snapshots__/Einschalten.test.ts.snap | 8 +- .../__snapshots__/Eurostreaming.test.ts.snap | 64 ++- src/source/__snapshots__/Frembed.test.ts.snap | 104 +++-- .../__snapshots__/FrenchCloud.test.ts.snap | 35 +- .../__snapshots__/HomeCine.test.ts.snap | 56 ++- src/source/__snapshots__/KinoGer.test.ts.snap | 64 ++- .../__snapshots__/MegaKino.test.ts.snap | 18 +- .../__snapshots__/MeineCloud.test.ts.snap | 28 +- .../__snapshots__/MostraGuarda.test.ts.snap | 28 +- src/source/__snapshots__/Movix.test.ts.snap | 200 +++++++--- .../__snapshots__/PrimeWire.test.ts.snap | 372 +++++++++++++++--- src/source/__snapshots__/Soaper.test.ts.snap | 32 +- .../__snapshots__/StreamKiste.test.ts.snap | 18 +- .../__snapshots__/VerHdLink.test.ts.snap | 63 ++- src/source/__snapshots__/VidSrc.test.ts.snap | 12 +- src/source/__snapshots__/VixSrc.test.ts.snap | 12 +- src/source/__snapshots__/XPrime.test.ts.snap | 16 +- src/types.ts | 4 +- src/utils/StreamResolver.test.ts | 2 +- src/utils/StreamResolver.ts | 16 +- 97 files changed, 1417 insertions(+), 790 deletions(-) diff --git a/src/extractor/DoodStream.test.ts b/src/extractor/DoodStream.test.ts index 4dfa3d1..32e04e2 100644 --- a/src/extractor/DoodStream.test.ts +++ b/src/extractor/DoodStream.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { DoodStream } from './DoodStream'; import { ExtractorRegistry } from './ExtractorRegistry'; @@ -12,22 +11,22 @@ const ctx = createTestContext(); describe('DoodStream', () => { test('dood.to', async () => { - expect(await extractorRegistry.handle(ctx, new URL('http://dood.to/e/sk1m9eumzyjj'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('http://dood.to/e/sk1m9eumzyjj'))).toMatchSnapshot(); }); test('doodster', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dooodster.com/e/1cfcevn6dg8shrfvht22odxw2lty18hr'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dooodster.com/e/1cfcevn6dg8shrfvht22odxw2lty18hr'))).toMatchSnapshot(); }); test('missing pass_md5 -> not found', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dood.to/e/gy8l8mb2i311'), CountryCode.mx)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dood.to/e/gy8l8mb2i311'))).toMatchSnapshot(); }); test('can guess height from title', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://do7go.com/e/dfx8me4un4ul'), CountryCode.fr)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://do7go.com/e/dfx8me4un4ul'))).toMatchSnapshot(); }); test('cloudflare storage', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://doodstream.com/e/wfpwtsgyr1xi'), CountryCode.mx)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://doodstream.com/e/wfpwtsgyr1xi'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/DoodStream.ts b/src/extractor/DoodStream.ts index a991852..77d190e 100644 --- a/src/extractor/DoodStream.ts +++ b/src/extractor/DoodStream.ts @@ -1,7 +1,7 @@ import * as cheerio from 'cheerio'; import randomstring from 'randomstring'; import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { guessSizeFromMp4 } from '../utils/size'; import { Extractor } from './Extractor'; @@ -23,7 +23,7 @@ export class DoodStream extends Extractor { return new URL(`http://dood.to/e/${videoId}`); }; - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const passMd5Match = html.match(/\/pass_md5\/[\w-]+\/([\w-]+)/); @@ -52,10 +52,10 @@ export class DoodStream extends Extractor { url: mp4Url, format: Format.mp4, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title, ...(bytes && { bytes }), }, diff --git a/src/extractor/Dropload.test.ts b/src/extractor/Dropload.test.ts index ce52d06..ad28e55 100644 --- a/src/extractor/Dropload.test.ts +++ b/src/extractor/Dropload.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { Dropload } from './Dropload'; import { ExtractorRegistry } from './ExtractorRegistry'; @@ -12,18 +11,18 @@ const ctx = createTestContext(); describe('Dropload', () => { test('dropload.io', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/embed-lyo2h1snpe5c.html'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/embed-lyo2h1snpe5c.html'))).toMatchSnapshot(); }); test('file not found', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'))).toMatchSnapshot(); }); test('processing / internal problem', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/7xtxuac84xyh.html'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/7xtxuac84xyh.html'))).toMatchSnapshot(); }); test('download URL', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/d/xhcmgcc2txnv'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://dropload.io/d/xhcmgcc2txnv'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Dropload.ts b/src/extractor/Dropload.ts index 6aacc84..958659a 100644 --- a/src/extractor/Dropload.ts +++ b/src/extractor/Dropload.ts @@ -1,7 +1,7 @@ import bytes from 'bytes'; import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { extractUrlFromPacked } from '../utils'; import { Extractor } from './Extractor'; @@ -18,7 +18,7 @@ export class Dropload extends Extractor { public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/d/', '/').replace('/e/', '/').replace('/embed-', '/')); - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); if (html.includes('File Not Found') || html.includes('Pending in queue')) { @@ -37,11 +37,11 @@ export class Dropload extends Extractor { url: extractUrlFromPacked(html, [/sources:\[{file:"(.*?)"/]), format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { + ...meta, bytes: bytes.parse(sizeMatch[1] as string) as number, - countryCodes: [countryCode], height: parseInt(heightMatch[1] as string), title, }, diff --git a/src/extractor/ExternalUrl.test.ts b/src/extractor/ExternalUrl.test.ts index bbe1820..8de4986 100644 --- a/src/extractor/ExternalUrl.test.ts +++ b/src/extractor/ExternalUrl.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExternalUrl } from './ExternalUrl'; import { ExtractorRegistry } from './ExtractorRegistry'; @@ -12,14 +11,10 @@ const ctx = createTestContext({ includeExternalUrls: 'on' }); describe('ExternalUrl', () => { test('404 - not found', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/gjA1OQ4klyHxgJ'), CountryCode.fr)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/gjA1OQ4klyHxgJ'))).toMatchSnapshot(); }); test('netu.fanstream.us', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://netu.fanstream.us/e/0DFgfkcXOsDP'), CountryCode.fr)).toMatchSnapshot(); - }); - - test('johnalwayssame.com with title', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://johnalwayssame.com/e/cqy9oue7sv0g'), CountryCode.fr, 'Black Mirror 4x2')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://netu.fanstream.us/e/0DFgfkcXOsDP'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/ExternalUrl.ts b/src/extractor/ExternalUrl.ts index 7481f17..e3f57b5 100644 --- a/src/extractor/ExternalUrl.ts +++ b/src/extractor/ExternalUrl.ts @@ -1,5 +1,5 @@ import { BlockedError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { showExternalUrls } from '../utils'; import { Extractor } from './Extractor'; @@ -14,7 +14,7 @@ export class ExternalUrl extends Extractor { return showExternalUrls(ctx.config) && null !== url.host.match(/.*/); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { try { // Make sure the URL is accessible, but avoid causing noise and delays doing this await this.fetcher.head(ctx, url, { noFlareSolverr: true, timeout: 1000, headers: { Referer: url.origin } }); @@ -31,12 +31,9 @@ export class ExternalUrl extends Extractor { format: Format.unknown, isExternal: true, label: `${url.host}`, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, - meta: { - countryCodes: [countryCode], - title, - }, + meta, }, ]; }; diff --git a/src/extractor/Extractor.ts b/src/extractor/Extractor.ts index d19bf6d..2668039 100644 --- a/src/extractor/Extractor.ts +++ b/src/extractor/Extractor.ts @@ -1,5 +1,5 @@ import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { Fetcher } from '../utils'; export abstract class Extractor { @@ -23,11 +23,11 @@ export abstract class Extractor { return url; }; - protected abstract extractInternal(ctx: Context, url: URL, countryCode: CountryCode, title?: string | undefined): Promise; + protected abstract extractInternal(ctx: Context, url: URL, meta?: Meta): Promise; - public async extract(ctx: Context, url: URL, countryCode: CountryCode, title?: string | undefined): Promise { + public async extract(ctx: Context, url: URL, meta?: Meta): Promise { try { - return await this.extractInternal(ctx, url, countryCode, title); + return await this.extractInternal(ctx, url, meta); } catch (error) { if (error instanceof NotFoundError) { return []; @@ -41,10 +41,7 @@ export abstract class Extractor { error, label: this.label, sourceId: `${this.id}`, - meta: { - countryCodes: [countryCode], - ...(title && { title }), - }, + ...(meta && meta), }, ]; } diff --git a/src/extractor/ExtractorRegistry.test.ts b/src/extractor/ExtractorRegistry.test.ts index 4d34654..61d2f1f 100644 --- a/src/extractor/ExtractorRegistry.test.ts +++ b/src/extractor/ExtractorRegistry.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { createExtractors } from './index'; @@ -12,46 +11,41 @@ describe('ExtractorRegistry', () => { const ctx = createTestContext(); test('returns error result from extractor', async () => { - const urlResult = await extractorRegistry.handle(ctx, new URL('https://some-url.test'), CountryCode.de); + const urlResult = await extractorRegistry.handle(ctx, new URL('https://some-url.test')); expect(urlResult).toMatchSnapshot(); }); test('returns external URLs if enabled by config', async () => { - const urlResult = await extractorRegistry.handle({ ...ctx, config: { ...ctx.config, includeExternalUrls: 'on' } }, new URL('https://mixdrop.ag/e/3nzwveprim63or6'), CountryCode.de); + const urlResult = await extractorRegistry.handle({ ...ctx, config: { ...ctx.config, includeExternalUrls: 'on' } }, new URL('https://mixdrop.ag/e/3nzwveprim63or6')); expect(urlResult).toMatchSnapshot(); }); test('does not return external URLs by default', async () => { - const urlResult = await extractorRegistry.handle(ctx, new URL('https://mixdrop.ag/e/l7v73zqrfdj19z'), CountryCode.de); + const urlResult = await extractorRegistry.handle(ctx, new URL('https://mixdrop.ag/e/l7v73zqrfdj19z')); expect(urlResult).toStrictEqual([]); }); test('returns from memory cache if possible', async () => { - const urlResults1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), CountryCode.de); - const urlResults2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html'), CountryCode.de); + const urlResults1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html')); + const urlResults2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/lyo2h1snpe5c.html')); expect(urlResults1).not.toStrictEqual([]); expect(urlResults2).not.toStrictEqual([]); }); test('ignores not found errors but caches them', async () => { - const urlResults1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), CountryCode.de); - const urlResults2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html'), CountryCode.de); + const urlResults1 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html')); + const urlResults2 = await extractorRegistry.handle(ctx, new URL('https://dropload.io/asdfghijklmn.html')); expect(urlResults1).toStrictEqual([]); expect(urlResults2).toStrictEqual([]); }); test('returns external url for error', async () => { - const urlResults = await extractorRegistry.handle(ctx, new URL('https://dropload.io/mocked-blocked.html'), CountryCode.de); - expect(urlResults).toMatchSnapshot(); - }); - - test('returns external url for error with title', async () => { - const urlResults = await extractorRegistry.handle(ctx, new URL('https://dropload.io/mocked-blocked-2.html'), CountryCode.de, 'a title!'); + const urlResults = await extractorRegistry.handle(ctx, new URL('https://dropload.io/mocked-blocked.html')); expect(urlResults).toMatchSnapshot(); }); diff --git a/src/extractor/ExtractorRegistry.ts b/src/extractor/ExtractorRegistry.ts index 79bf3d8..251049e 100644 --- a/src/extractor/ExtractorRegistry.ts +++ b/src/extractor/ExtractorRegistry.ts @@ -2,7 +2,7 @@ import KeyvSqlite from '@keyv/sqlite'; import { Cacheable, CacheableMemory, Keyv } from 'cacheable'; import winston from 'winston'; -import { Context, CountryCode, UrlResult } from '../types'; +import { Context, Meta, UrlResult } from '../types'; import { getCacheDir, isExtractorDisabled } from '../utils'; import { Extractor } from './Extractor'; @@ -28,7 +28,7 @@ export class ExtractorRegistry { }; }; - public async handle(ctx: Context, url: URL, countryCode: CountryCode, title?: string | undefined): Promise { + public async handle(ctx: Context, url: URL, meta?: Meta): Promise { const extractor = this.extractors.find(extractor => !isExtractorDisabled(ctx.config, extractor) && extractor.supports(ctx, url)); if (!extractor) { return []; @@ -54,7 +54,7 @@ export class ExtractorRegistry { this.logger.info(`Extract stream URL using ${extractor.id} extractor from ${url}`, ctx); - const urlResults = await extractor.extract(ctx, normalizedUrl, countryCode, title); + const urlResults = await extractor.extract(ctx, normalizedUrl, meta || { countryCodes: [] }); if (!urlResults.some(urlResult => urlResult.error) && extractor.ttl) { await this.urlResultCache.set(cacheKey, urlResults, extractor.ttl); diff --git a/src/extractor/Fastream.test.ts b/src/extractor/Fastream.test.ts index 75185c2..c708747 100644 --- a/src/extractor/Fastream.test.ts +++ b/src/extractor/Fastream.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Fastream } from './Fastream'; @@ -12,6 +11,6 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow-proxy.test describe('Fastream', () => { test('fastream.to embed', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://fastream.to/embed-3aooif4ozt10.html'), CountryCode.es)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://fastream.to/embed-3aooif4ozt10.html'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Fastream.ts b/src/extractor/Fastream.ts index 93968fc..6ad4e58 100644 --- a/src/extractor/Fastream.ts +++ b/src/extractor/Fastream.ts @@ -1,5 +1,5 @@ import bytes from 'bytes'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { buildMediaFlowProxyExtractorStreamUrl, supportsMediaFlowProxy } from '../utils'; import { Extractor } from './Extractor'; @@ -18,7 +18,7 @@ export class Fastream extends Extractor { return new URL(url.href.replace('/e/', '/embed-').replace('/d/', '/embed-')); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const playlistUrl = await buildMediaFlowProxyExtractorStreamUrl(ctx, this.fetcher, 'Fastream', url); const downloadUrl = new URL(url.href.replace('/embed-', '/d/')); @@ -32,10 +32,10 @@ export class Fastream extends Extractor { url: playlistUrl, format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, bytes: bytes.parse(heightAndSizeMatch[2] as string) as number, height: parseInt(heightAndSizeMatch[1] as string), title: titleMatch[1], diff --git a/src/extractor/Fsst.test.ts b/src/extractor/Fsst.test.ts index 085285d..bb0764c 100644 --- a/src/extractor/Fsst.test.ts +++ b/src/extractor/Fsst.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Fsst } from './Fsst'; @@ -12,14 +11,14 @@ const ctx = createTestContext(); describe('Fsst', () => { test('Blood & Sinners', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/900576/'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/900576/'))).toMatchSnapshot(); }); test('Dead City', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/901994/'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/901994/'))).toMatchSnapshot(); }); test('How to Train Your Dragon', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/902668/'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://fsst.online/embed/902668/'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Fsst.ts b/src/extractor/Fsst.ts index b3d13ce..b70f8d8 100644 --- a/src/extractor/Fsst.ts +++ b/src/extractor/Fsst.ts @@ -1,5 +1,5 @@ import * as cheerio from 'cheerio'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; export class Fsst extends Extractor { @@ -11,7 +11,7 @@ export class Fsst extends Extractor { return null !== url.host.match(/fsst/); }; - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const $ = cheerio.load(html); @@ -19,7 +19,7 @@ export class Fsst extends Extractor { const filesMatch = html.match(/file:"(.*)"/) as string[]; - return (filesMatch[1] as string).split(',').map((fileString, index) => { + return (filesMatch[1] as string).split(',').map((fileString) => { const heightAndUrlMatch = fileString.match(/\[?([\d]*)p?]?(.*)/) as string[]; const fileHref = heightAndUrlMatch[2] as string; const heightFromFileHrefMatch = fileHref.match(/([\d]+)p/) as string[]; @@ -28,9 +28,9 @@ export class Fsst extends Extractor { url: new URL(fileHref), format: Format.mp4, label: this.label, - sourceId: `${this.id}_${countryCode}_${index}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, meta: { - countryCodes: [countryCode], + ...meta, height: parseInt(heightAndUrlMatch[1] || heightFromFileHrefMatch[1] as string), title, }, diff --git a/src/extractor/KinoGer.test.ts b/src/extractor/KinoGer.test.ts index 9b5ad1a..fa6085a 100644 --- a/src/extractor/KinoGer.test.ts +++ b/src/extractor/KinoGer.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { KinoGer } from './KinoGer'; @@ -12,10 +11,10 @@ const ctx = createTestContext(); describe('KinoGer', () => { test('Blood & Sinners', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://kinoger.re/#ge5fhb'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://kinoger.re/#ge5fhb'))).toMatchSnapshot(); }); test('Dead City', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://kinoger.re/#x6tsx9'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://kinoger.re/#x6tsx9'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/KinoGer.ts b/src/extractor/KinoGer.ts index c1cde44..f7ad9e2 100644 --- a/src/extractor/KinoGer.ts +++ b/src/extractor/KinoGer.ts @@ -1,5 +1,5 @@ import crypto from 'crypto'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { guessHeightFromPlaylist } from '../utils'; import { Extractor } from './Extractor'; @@ -35,7 +35,7 @@ export class KinoGer extends Extractor { return new URL(`${url.origin}/api/v1/video?id=${url.hash.slice(1)}`); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const hexData = await this.fetcher.text(ctx, url, { headers: { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' } }); const encrypted = Buffer.from(hexData, 'hex'); @@ -53,10 +53,10 @@ export class KinoGer extends Extractor { url: new URL(cf), format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url, { headers: { Referer: url.origin } }), title, }, diff --git a/src/extractor/Mixdrop.test.ts b/src/extractor/Mixdrop.test.ts index 78035df..e8e6046 100644 --- a/src/extractor/Mixdrop.test.ts +++ b/src/extractor/Mixdrop.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Mixdrop } from './Mixdrop'; @@ -12,10 +11,10 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow-proxy.test describe('Mixdrop', () => { test('mixdrop.my /e/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://mixdrop.my/e/knq0kj8waq44l8'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://mixdrop.my/e/knq0kj8waq44l8'))).toMatchSnapshot(); }); test('deleted or expired file', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://mixdrop.ag/e/123456789'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://mixdrop.ag/e/123456789'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Mixdrop.ts b/src/extractor/Mixdrop.ts index 5f60067..0c603d5 100644 --- a/src/extractor/Mixdrop.ts +++ b/src/extractor/Mixdrop.ts @@ -1,7 +1,7 @@ import bytes from 'bytes'; import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils'; import { Extractor } from './Extractor'; @@ -18,7 +18,7 @@ export class Mixdrop extends Extractor { public override readonly normalize = (url: URL): URL => new URL(url.href.replace('/f/', '/e/')); - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const fileUrl = new URL(url.href.replace('/e/', '/f/')); const html = await this.fetcher.text(ctx, fileUrl); @@ -36,10 +36,10 @@ export class Mixdrop extends Extractor { url: buildMediaFlowProxyExtractorRedirectUrl(ctx, 'Mixdrop', url), format: Format.mp4, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, bytes: bytes.parse((sizeMatch[1] as string).replace(',', '')) as number, title, }, diff --git a/src/extractor/SaveFiles.test.ts b/src/extractor/SaveFiles.test.ts index 9078ecc..8213b16 100644 --- a/src/extractor/SaveFiles.test.ts +++ b/src/extractor/SaveFiles.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { SaveFiles } from './SaveFiles'; @@ -12,22 +11,22 @@ const ctx = createTestContext(); describe('SafeFiles', () => { test('savefiles', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/ip0k0dj2g0i3'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/ip0k0dj2g0i3'))).toMatchSnapshot(); }); test('savefiles /e/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/e/edptfmhyjr39'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/e/edptfmhyjr39'))).toMatchSnapshot(); }); test('savefiles /d/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/d/s9g6zb5kjbqd'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/d/s9g6zb5kjbqd'))).toMatchSnapshot(); }); test('savefiles locked file', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/omqq55i59nvv'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://savefiles.com/omqq55i59nvv'))).toMatchSnapshot(); }); test('streamhls /e/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://streamhls.to/e/ip0k0dj2g0i3'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://streamhls.to/e/ip0k0dj2g0i3'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/SaveFiles.ts b/src/extractor/SaveFiles.ts index efd98c8..c9bb22f 100644 --- a/src/extractor/SaveFiles.ts +++ b/src/extractor/SaveFiles.ts @@ -1,6 +1,6 @@ import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; export class SaveFiles extends Extractor { @@ -18,7 +18,7 @@ export class SaveFiles extends Extractor { return new URL(url.href.replace('/e/', '/').replace('/d/', '/')); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); if (/File was locked by administrator/.test(html)) { @@ -36,10 +36,10 @@ export class SaveFiles extends Extractor { url: new URL(fileMatch[1] as string), format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title, height: parseInt(sizeMatch[1] as string), }, diff --git a/src/extractor/Soaper.test.ts b/src/extractor/Soaper.test.ts index 6330a22..d13d90d 100644 --- a/src/extractor/Soaper.test.ts +++ b/src/extractor/Soaper.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Soaper } from './Soaper'; @@ -12,14 +11,14 @@ const ctx = createTestContext(); describe('Soaper', () => { test('Full Metal Jacket', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/movie_d8kdeypDY9.html'), CountryCode.en, 'Full Metal Jacket (1987)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/movie_d8kdeypDY9.html'))).toMatchSnapshot(); }); test('Black Mirror', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/episode_5KDq78eGp1.html'), CountryCode.en, 'Black Mirror 4x2')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/episode_5KDq78eGp1.html'))).toMatchSnapshot(); }); test('last of us s2e3', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/episode_rYg3vMEDL1.html'), CountryCode.de, 'The Last of Us 2x3')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://soaper.live/episode_rYg3vMEDL1.html'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Soaper.ts b/src/extractor/Soaper.ts index 4801cc6..13174be 100644 --- a/src/extractor/Soaper.ts +++ b/src/extractor/Soaper.ts @@ -1,4 +1,4 @@ -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { guessHeightFromPlaylist } from '../utils'; import { Extractor } from './Extractor'; @@ -18,7 +18,7 @@ export class Soaper extends Extractor { return null !== url.host.match(/soaper/) && null !== url.pathname.match(/^\/(episode|movie)_/); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode, title: string | undefined): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const movieOrEpisodeId = (url.pathname.match(/\/\w+_(\w+)/) as string[])[1] as string; const form = new URLSearchParams(); @@ -48,12 +48,11 @@ export class Soaper extends Extractor { url: m3u8Url, format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url), - title: `${title}`, }, }, ]; diff --git a/src/extractor/StreamEmbed.test.ts b/src/extractor/StreamEmbed.test.ts index d856a9c..9118815 100644 --- a/src/extractor/StreamEmbed.test.ts +++ b/src/extractor/StreamEmbed.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { StreamEmbed } from './StreamEmbed'; @@ -12,6 +11,6 @@ const ctx = createTestContext(); describe('StreamEmbed', () => { test('watch.gxplayer.xyz', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://watch.gxplayer.xyz/watch?v=MEKI92PU'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://watch.gxplayer.xyz/watch?v=MEKI92PU'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/StreamEmbed.ts b/src/extractor/StreamEmbed.ts index 84fb133..25587cb 100644 --- a/src/extractor/StreamEmbed.ts +++ b/src/extractor/StreamEmbed.ts @@ -1,4 +1,4 @@ -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; export class StreamEmbed extends Extractor { @@ -10,7 +10,7 @@ export class StreamEmbed extends Extractor { return null !== url.host.match(/bullstream|mp4player|watch\.gxplayer/); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const video = JSON.parse((html.match(/video ?= ?(.*);/) as string[])[1] as string); @@ -20,10 +20,10 @@ export class StreamEmbed extends Extractor { url: new URL(`/m3u8/${video.uid}/${video.md5}/master.txt?s=1&id=${video.id}&cache=${video.status}`, url.origin), format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, height: parseInt(JSON.parse(video.quality)[0]), title: decodeURIComponent(video.title), }, diff --git a/src/extractor/Streamtape.test.ts b/src/extractor/Streamtape.test.ts index cb845fc..15bb144 100644 --- a/src/extractor/Streamtape.test.ts +++ b/src/extractor/Streamtape.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Streamtape } from './Streamtape'; @@ -12,6 +11,6 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow.test.org', describe('Streamtape', () => { test('streamtape.com /e', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/84Kb3mkoYAiokmW'), CountryCode.es)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://streamtape.com/e/84Kb3mkoYAiokmW'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Streamtape.ts b/src/extractor/Streamtape.ts index 45348f4..d90c166 100644 --- a/src/extractor/Streamtape.ts +++ b/src/extractor/Streamtape.ts @@ -1,5 +1,5 @@ import * as cheerio from 'cheerio'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy, @@ -16,7 +16,7 @@ export class Streamtape extends Extractor { return null !== url.host.match(/streamtape/) && supportsMediaFlowProxy(ctx); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const $ = cheerio.load(html); @@ -29,10 +29,10 @@ export class Streamtape extends Extractor { url: mp4Url, format: Format.mp4, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title, bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url), }, diff --git a/src/extractor/SuperVideo.test.ts b/src/extractor/SuperVideo.test.ts index 6a0326c..e2b8aca 100644 --- a/src/extractor/SuperVideo.test.ts +++ b/src/extractor/SuperVideo.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { SuperVideo } from './SuperVideo'; @@ -12,22 +11,22 @@ const ctx = createTestContext(); describe('SuperVideo', () => { test('supervideo.cc /e/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/e/q7i0sw1oytw3'), CountryCode.de)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/e/q7i0sw1oytw3'))).toMatchSnapshot(); }); test('supervideo.tv /embed-/', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.tv/embed-1p0m1fi9mok8.html'), CountryCode.it)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.tv/embed-1p0m1fi9mok8.html'))).toMatchSnapshot(); }); test('embed only', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/e/bj6szat1pval'), CountryCode.it)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/e/bj6szat1pval'))).toMatchSnapshot(); }); test('deleted or expired file', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/ndf5shmy9lpt'), CountryCode.it)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/ndf5shmy9lpt'))).toMatchSnapshot(); }); test('processing video', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/3h1qqoqtldo8'), CountryCode.it)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://supervideo.cc/3h1qqoqtldo8'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/SuperVideo.ts b/src/extractor/SuperVideo.ts index 85a370a..1dd28cd 100644 --- a/src/extractor/SuperVideo.ts +++ b/src/extractor/SuperVideo.ts @@ -1,7 +1,7 @@ import bytes from 'bytes'; import * as cheerio from 'cheerio'; import { NotFoundError } from '../error'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { extractUrlFromPacked, guessHeightFromPlaylist } from '../utils'; import { Extractor } from './Extractor'; @@ -20,11 +20,11 @@ export class SuperVideo extends Extractor { return new URL(url.href.replace('/e/', '/').replace('/embed-', '/')); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); if (html.includes('This video can be watched as embed only')) { - return await this.extractInternal(ctx, new URL(`/e${url.pathname}`, url.origin), countryCode); + 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)) { @@ -47,10 +47,10 @@ export class SuperVideo extends Extractor { url: m3u8Url, format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title, ...(size && { bytes: size }), ...(height && { height }), diff --git a/src/extractor/Uqload.test.ts b/src/extractor/Uqload.test.ts index bd81d63..27009e7 100644 --- a/src/extractor/Uqload.test.ts +++ b/src/extractor/Uqload.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { Uqload } from './Uqload'; @@ -12,6 +11,6 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow.test.org', describe('Uqload', () => { test('uqload.net /embed-', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://uqload.net/embed-z0xbr87oz637.html'), CountryCode.fr)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://uqload.net/embed-z0xbr87oz637.html'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/Uqload.ts b/src/extractor/Uqload.ts index e899eb2..4d9f26d 100644 --- a/src/extractor/Uqload.ts +++ b/src/extractor/Uqload.ts @@ -1,5 +1,5 @@ import * as cheerio from 'cheerio'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { buildMediaFlowProxyExtractorRedirectUrl, supportsMediaFlowProxy } from '../utils'; import { guessSizeFromMp4 } from '../utils/size'; import { Extractor } from './Extractor'; @@ -19,7 +19,7 @@ export class Uqload extends Extractor { return new URL(url.href.replace('/embed-', '/')); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const heightMatch = html.match(/\d{3,}x(\d{3,})/); @@ -34,10 +34,10 @@ export class Uqload extends Extractor { url: mp4Url, format: Format.mp4, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title, bytes: await guessSizeFromMp4(ctx, this.fetcher, mp4Url), ...(heightMatch && { diff --git a/src/extractor/VidSrc.test.ts b/src/extractor/VidSrc.test.ts index 9dc912f..3f34a3a 100644 --- a/src/extractor/VidSrc.test.ts +++ b/src/extractor/VidSrc.test.ts @@ -1,7 +1,6 @@ import { MockAgent, setGlobalDispatcher } from 'undici'; import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { Fetcher, FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { VidSrc } from './VidSrc'; @@ -13,15 +12,15 @@ const ctx = createTestContext(); describe('VidSrc', () => { test('Full Metal Jacket', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/movie/tt0093058'), CountryCode.en, 'Full Metal Jacket (1987)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/movie/tt0093058'))).toMatchSnapshot(); }); test('Black Mirror', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/tv/tt2085059/4/2'), CountryCode.en, 'Black Mirror 4x2')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/tv/tt2085059/4/2'))).toMatchSnapshot(); }); test('not found', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/movie/tt35628853'), CountryCode.en, 'Titan: The OceanGate Disaster')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vidsrc.xyz/embed/movie/tt35628853'))).toMatchSnapshot(); }); test('rate limit issues are retried and fail if no tlds are left', async () => { @@ -37,6 +36,6 @@ describe('VidSrc', () => { const fetcher = new Fetcher(winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] })); const vidSrc = new VidSrc(fetcher, ['net', 'xyz']); - expect(await vidSrc.extract(ctx, new URL('https://vidsrc.xyz/embed/movie/tt33043892/1/1'), CountryCode.en, 'Dexter: Resurrection 1x1')).toMatchSnapshot(); + expect(await vidSrc.extract(ctx, new URL('https://vidsrc.xyz/embed/movie/tt33043892/1/1'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/VidSrc.ts b/src/extractor/VidSrc.ts index 9783960..0540897 100644 --- a/src/extractor/VidSrc.ts +++ b/src/extractor/VidSrc.ts @@ -1,7 +1,7 @@ import * as cheerio from 'cheerio'; import slugify from 'slugify'; import { NotFoundError, TooManyRequestsError } from '../error'; -import { Context, CountryCode, Format, NonEmptyArray, UrlResult } from '../types'; +import { Context, Format, Meta, NonEmptyArray, UrlResult } from '../types'; import { Fetcher, guessHeightFromPlaylist } from '../utils'; import { Extractor } from './Extractor'; @@ -24,11 +24,11 @@ export class VidSrc extends Extractor { return null !== url.host.match(/vidsrc/); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { - return this.extractUsingRandomTld(ctx, url, countryCode, [...this.tlds]); + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { + return this.extractUsingRandomTld(ctx, url, meta, [...this.tlds]); }; - private async extractUsingRandomTld(ctx: Context, url: URL, countryCode: CountryCode, tlds: string[]): Promise { + private async extractUsingRandomTld(ctx: Context, url: URL, meta: Meta, tlds: string[]): Promise { const tldIndex = Math.floor(Math.random() * tlds.length); const [tld] = tlds.splice(tldIndex, 1) as [string]; @@ -42,7 +42,7 @@ export class VidSrc extends Extractor { html = await this.fetcher.text(ctx, newUrl); } catch (error) { if (error instanceof TooManyRequestsError && tlds.length) { - return this.extractUsingRandomTld(ctx, url, countryCode, tlds); + return this.extractUsingRandomTld(ctx, url, meta, tlds); } throw error; @@ -74,10 +74,10 @@ export class VidSrc extends Extractor { url: m3u8Url, format: Format.hls, label: `${this.label} (${serverName})`, - sourceId: `${this.id}_${slugify(serverName)}_${countryCode}`, + sourceId: `${this.id}_${slugify(serverName)}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, height: await guessHeightFromPlaylist(ctx, this.fetcher, m3u8Url), title, }, diff --git a/src/extractor/VixSrc.test.ts b/src/extractor/VixSrc.test.ts index 2c9abb4..3f01d0e 100644 --- a/src/extractor/VixSrc.test.ts +++ b/src/extractor/VixSrc.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { VixSrc } from './VixSrc'; @@ -12,16 +11,16 @@ const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow-proxy.test describe('VixSrc', () => { test('Full Metal Jacket', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/movie/600'), CountryCode.it, 'Full Metal Jacket (1987)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/movie/600'))).toMatchSnapshot(); }); test('Black Mirror', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/tv/42009/4/2'), CountryCode.it, 'Black Mirror 4x2')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/tv/42009/4/2'))).toMatchSnapshot(); }); test('Black Mirror is excluded if no matching language was found', async () => { const ctx = createTestContext({ mediaFlowProxyUrl: 'https://mediaflow-proxy2.test', mediaFlowProxyPassword: 'asdfg', de: 'on' }); - expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/tv/42009/4/2'), CountryCode.it, 'Black Mirror 4x2')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://vixsrc.to/tv/42009/4/2'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/VixSrc.ts b/src/extractor/VixSrc.ts index 5776a8a..521ebd0 100644 --- a/src/extractor/VixSrc.ts +++ b/src/extractor/VixSrc.ts @@ -1,5 +1,5 @@ import bytes from 'bytes'; -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, CountryCode, Format, Meta, UrlResult } from '../types'; import { buildMediaFlowProxyExtractorStreamUrl, hasMultiEnabled, @@ -19,7 +19,7 @@ export class VixSrc extends Extractor { return null !== url.host.match(/vixsrc/) && supportsMediaFlowProxy(ctx); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const filenameMatch = html.match(/"filename":"(.*?)"/); @@ -38,7 +38,7 @@ export class VixSrc extends Extractor { url: playlistUrl, format: Format.hls, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { countryCodes, diff --git a/src/extractor/XPrime.test.ts b/src/extractor/XPrime.test.ts index 51d9e7f..b6e40fd 100644 --- a/src/extractor/XPrime.test.ts +++ b/src/extractor/XPrime.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { XPrime } from './XPrime'; @@ -12,18 +11,18 @@ const ctx = createTestContext(); describe('XPrime', () => { test('unknown backend is ignored', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/unknown?name=Superman&year=2025'), CountryCode.en, 'Superman (2025)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/unknown?name=Superman&year=2025'))).toMatchSnapshot(); }); test('primebox Superman', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Superman&year=2025'), CountryCode.en, 'Superman (2025)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Superman&year=2025'))).toMatchSnapshot(); }); test('primebox Alien: Earth', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Alien%3A%20Earth&year=2025&season=1&episode=1'), CountryCode.en, 'Alien: Earth 1x1')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Alien%3A%20Earth&year=2025&season=1&episode=1'))).toMatchSnapshot(); }); test('primebox not found', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Jurassic%20World%20Rebirth&year=2025'), CountryCode.en, 'Jurassic World Rebirth (2025)')).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://backend.xprime.tv/primebox?name=Jurassic%20World%20Rebirth&year=2025'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/XPrime.ts b/src/extractor/XPrime.ts index 078fb75..a44f018 100644 --- a/src/extractor/XPrime.ts +++ b/src/extractor/XPrime.ts @@ -1,4 +1,4 @@ -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { guessSizeFromMp4 } from '../utils/size'; import { Extractor } from './Extractor'; @@ -24,7 +24,7 @@ export class XPrime extends Extractor { return null !== url.host.match(/xprime/); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const urlResults: UrlResult[] = []; const referer = url.protocol + '//' + url.hostname.split('.').slice(-2).join('.'); // Strip subdomains @@ -42,10 +42,10 @@ export class XPrime extends Extractor { url, format: Format.mp4, label: `${this.label} Primebox`, - sourceId: `${this.id}_${countryCode}_primebox`, + sourceId: `${this.id}_primebox_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, bytes: await guessSizeFromMp4(ctx, this.fetcher, url, { headers: { Referer: referer }, minCacheTtl: this.ttl }), height: parseInt(resolution), title, diff --git a/src/extractor/YouTube.test.ts b/src/extractor/YouTube.test.ts index b1a0c6a..9e869b2 100644 --- a/src/extractor/YouTube.test.ts +++ b/src/extractor/YouTube.test.ts @@ -1,6 +1,5 @@ import winston from 'winston'; import { createTestContext } from '../test'; -import { CountryCode } from '../types'; import { FetcherMock } from '../utils'; import { ExtractorRegistry } from './ExtractorRegistry'; import { YouTube } from './YouTube'; @@ -12,10 +11,10 @@ const ctx = createTestContext(); describe('YouTube', () => { test('Solaris', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://www.youtube.com/watch?v=Z8ZhQPaw4rE'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://www.youtube.com/watch?v=Z8ZhQPaw4rE'))).toMatchSnapshot(); }); test('unsupported format', async () => { - expect(await extractorRegistry.handle(ctx, new URL('https://youtu.be/Z8ZhQPaw4rE?feature=shared'), CountryCode.en)).toMatchSnapshot(); + expect(await extractorRegistry.handle(ctx, new URL('https://youtu.be/Z8ZhQPaw4rE?feature=shared'))).toMatchSnapshot(); }); }); diff --git a/src/extractor/YouTube.ts b/src/extractor/YouTube.ts index b156db4..a6a7ed7 100644 --- a/src/extractor/YouTube.ts +++ b/src/extractor/YouTube.ts @@ -1,4 +1,4 @@ -import { Context, CountryCode, Format, UrlResult } from '../types'; +import { Context, Format, Meta, UrlResult } from '../types'; import { Extractor } from './Extractor'; export class YouTube extends Extractor { @@ -12,7 +12,7 @@ export class YouTube extends Extractor { return null !== url.host.match(/youtube/) && url.searchParams.has('v'); } - protected async extractInternal(ctx: Context, url: URL, countryCode: CountryCode): Promise { + protected async extractInternal(ctx: Context, url: URL, meta: Meta): Promise { const html = await this.fetcher.text(ctx, url); const titleMatch = html.match(/"title":{"runs":\[{"text":"(.*?)"/) as string[]; @@ -23,10 +23,10 @@ export class YouTube extends Extractor { format: Format.unknown, ytId: url.searchParams.get('v') as string, label: this.label, - sourceId: `${this.id}_${countryCode}`, + sourceId: `${this.id}_${meta.countryCodes?.join('_')}`, ttl: this.ttl, meta: { - countryCodes: [countryCode], + ...meta, title: titleMatch[1] as string, }, }, diff --git a/src/extractor/__snapshots__/DoodStream.test.ts.snap b/src/extractor/__snapshots__/DoodStream.test.ts.snap index b46ee1e..b8c1099 100644 --- a/src/extractor/__snapshots__/DoodStream.test.ts.snap +++ b/src/extractor/__snapshots__/DoodStream.test.ts.snap @@ -6,15 +6,13 @@ exports[`DoodStream can guess height from title 1`] = ` "format": "mp4", "label": "DoodStream", "meta": { - "countryCodes": [ - "fr", - ], + "countryCodes": [], "title": "Black Mirror S04E02 FRENCH 720p WEB x264-RiPiT", }, "requestHeaders": { "Referer": "http://dood.to", }, - "sourceId": "doodstream_fr", + "sourceId": "doodstream_", "ttl": 21600000, "url": "https://ee317r.cloudatacdn.com/u5kj63yvxddlsdgge7qremqqka27irwiupc4k7o5cikbhjliz6xwv2xr53uq/lco4d3heif~mocked-random-string?token=fh3rdlhjvao7chgxndsi3ra7&expiry=639837296000", }, @@ -27,15 +25,13 @@ exports[`DoodStream cloudflare storage 1`] = ` "format": "mp4", "label": "DoodStream", "meta": { - "countryCodes": [ - "mx", - ], + "countryCodes": [], "title": "241653--5bf448d3-184a-4b3f-a766-fa98d93c1300--bllz--2100527-doodstream", }, "requestHeaders": { "Referer": "http://dood.to", }, - "sourceId": "doodstream_mx", + "sourceId": "doodstream_", "ttl": 21600000, "url": "https://wec6bnh7rx7rw11bv9e9vslw4rud8nam.5f1ebd98099ce35faeeddb30c1752191.r2.cloudflarestorage.com/icxxqqdsfdc4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ab085b515b950aae0a86cae59456cacd%2F20250714%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250714T154400Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=845ebfa5feedd840027e431f31e013513dd4c4f74364b4e75a2c8ddb213bd97a", }, @@ -49,15 +45,13 @@ exports[`DoodStream dood.to 1`] = ` "label": "DoodStream", "meta": { "bytes": 791702409, - "countryCodes": [ - "de", - ], + "countryCodes": [], "title": "des-teufels-bad-2024", }, "requestHeaders": { "Referer": "http://dood.to", }, - "sourceId": "doodstream_de", + "sourceId": "doodstream_", "ttl": 21600000, "url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rlyusvl46iz3u3ta/1f4isxg0tw~mocked-random-string?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000", }, @@ -70,15 +64,13 @@ exports[`DoodStream doodster 1`] = ` "format": "mp4", "label": "DoodStream", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "title": "Brokeback Mountain 2005", }, "requestHeaders": { "Referer": "http://dood.to", }, - "sourceId": "doodstream_de", + "sourceId": "doodstream_", "ttl": 21600000, "url": "https://ijs155l.cloudatacdn.com/u5kj2rz5qtdlsdgge4hrgj2flz5cjodea6auatveaphxogr2vvukxfzmwzva/egprvdjvpr~mocked-random-string?token=fbsg3301phkef2s4swo8bssg&expiry=639837296000", }, diff --git a/src/extractor/__snapshots__/Dropload.test.ts.snap b/src/extractor/__snapshots__/Dropload.test.ts.snap index 063d564..edb0474 100644 --- a/src/extractor/__snapshots__/Dropload.test.ts.snap +++ b/src/extractor/__snapshots__/Dropload.test.ts.snap @@ -7,13 +7,11 @@ exports[`Dropload download URL 1`] = ` "label": "Dropload", "meta": { "bytes": 333027737, - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, "title": "[~Squid~Game~EnG~]~S03E01~720p~WebRip ~❤️GLD❤️~", }, - "sourceId": "dropload_en", + "sourceId": "dropload_", "ttl": 10800000, "url": "https://srv29.dropload.io/hls2/01/00305/xhcmgcc2txnv_h/master.m3u8?t=MBKiIoVlA42YcZEixT7ZR1xJA5qKhcKgBi84GPmyT7U&s=1752613129&e=14400&f=1526758&i=0.0&sp=0&ii=130.61.236.204", }, @@ -27,13 +25,11 @@ exports[`Dropload dropload.io 1`] = ` "label": "Dropload", "meta": { "bytes": 1395864371, - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 1080, "title": "des-teufels-bad-2024", }, - "sourceId": "dropload_de", + "sourceId": "dropload_", "ttl": 10800000, "url": "https://srv34.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=ESjD9Le53ttKN2JPyxrhLUqTebDZvx3vcQUlNgP6Vko&s=1749845553&e=14400&f=987607&srv=srv27&i=0.0&sp=0&ii=50.99.15.93&p1=srv27&p2=srv27", }, diff --git a/src/extractor/__snapshots__/ExternalUrl.test.ts.snap b/src/extractor/__snapshots__/ExternalUrl.test.ts.snap index 5b50067..fa3e9a7 100644 --- a/src/extractor/__snapshots__/ExternalUrl.test.ts.snap +++ b/src/extractor/__snapshots__/ExternalUrl.test.ts.snap @@ -2,25 +2,6 @@ exports[`ExternalUrl 404 - not found 1`] = `[]`; -exports[`ExternalUrl johnalwayssame.com with title 1`] = ` -[ - { - "format": "unknown", - "isExternal": true, - "label": "johnalwayssame.com", - "meta": { - "countryCodes": [ - "fr", - ], - "title": "Black Mirror 4x2", - }, - "sourceId": "external_fr", - "ttl": 21600000, - "url": "https://johnalwayssame.com/e/cqy9oue7sv0g", - }, -] -`; - exports[`ExternalUrl netu.fanstream.us 1`] = ` [ { @@ -28,12 +9,9 @@ exports[`ExternalUrl netu.fanstream.us 1`] = ` "isExternal": true, "label": "netu.fanstream.us", "meta": { - "countryCodes": [ - "fr", - ], - "title": undefined, + "countryCodes": [], }, - "sourceId": "external_fr", + "sourceId": "external_", "ttl": 21600000, "url": "https://netu.fanstream.us/e/0DFgfkcXOsDP", }, diff --git a/src/extractor/__snapshots__/ExtractorRegistry.test.ts.snap b/src/extractor/__snapshots__/ExtractorRegistry.test.ts.snap index a0d8a67..c6fb508 100644 --- a/src/extractor/__snapshots__/ExtractorRegistry.test.ts.snap +++ b/src/extractor/__snapshots__/ExtractorRegistry.test.ts.snap @@ -9,12 +9,9 @@ exports[`ExtractorRegistry returns external URLs if enabled by config 1`] = ` "isExternal": true, "label": "mixdrop.ag", "meta": { - "countryCodes": [ - "de", - ], - "title": undefined, + "countryCodes": [], }, - "sourceId": "external_de", + "sourceId": "external_", "ttl": 21600000, "url": "https://mixdrop.ag/e/3nzwveprim63or6", }, @@ -24,38 +21,14 @@ exports[`ExtractorRegistry returns external URLs if enabled by config 1`] = ` exports[`ExtractorRegistry returns external url for error 1`] = ` [ { + "countryCodes": [], "error": [Error: Fetcher error: 403: Forbidden ], "format": "unknown", "isExternal": true, "label": "Dropload", - "meta": { - "countryCodes": [ - "de", - ], - }, "sourceId": "dropload", "url": "https://dropload.io/mocked-blocked.html", }, ] `; - -exports[`ExtractorRegistry returns external url for error with title 1`] = ` -[ - { - "error": [Error: Fetcher error: 403: Forbidden -], - "format": "unknown", - "isExternal": true, - "label": "Dropload", - "meta": { - "countryCodes": [ - "de", - ], - "title": "a title!", - }, - "sourceId": "dropload", - "url": "https://dropload.io/mocked-blocked-2.html", - }, -] -`; diff --git a/src/extractor/__snapshots__/Fastream.test.ts.snap b/src/extractor/__snapshots__/Fastream.test.ts.snap index b24ef2b..9d23f6b 100644 --- a/src/extractor/__snapshots__/Fastream.test.ts.snap +++ b/src/extractor/__snapshots__/Fastream.test.ts.snap @@ -7,13 +7,11 @@ exports[`Fastream fastream.to embed 1`] = ` "label": "Fastream (via MediaFlow Proxy)", "meta": { "bytes": 1073741824, - "countryCodes": [ - "es", - ], + "countryCodes": [], "height": 1080, "title": "black.mirror.s01e01.lat.720p.mp4", }, - "sourceId": "fastream_es", + "sourceId": "fastream_", "ttl": 900000, "url": "https://mediaflow-proxy.test/proxy/hls/manifest.m3u8?api_password=asdfg&h_user-agent=Mozilla%2F5.0+%28X11%3B+Linux+x86_64%3B+rv%3A138.0%29+Gecko%2F20100101+Firefox%2F138.0&h_Accept=*%2F*&h_Connection=keep-alive&h_Accept-Language=en-US%2Cen%3Bq%3D0.5&h_referer=https%3A%2F%2Ffastream.to%2F&h_origin=https%3A%2F%2Ffastream.to&d=https%3A%2F%2Fs28.fastream.to%2Fhls2%2F09%2F00024%2F3aooif4ozt10_%2Cl%2Cn%2Ch%2Cx%2C.urlset%2Fmaster.m3u8%3Ft%3Dnw6_aeN6pDROuXsJQwyrJVm3qHKFedVfbTNj5LgXH-4%26s%3D1752863379%26e%3D43200%26v%3D1058373%26i%3D0.3%26sp%3D0", }, diff --git a/src/extractor/__snapshots__/Fsst.test.ts.snap b/src/extractor/__snapshots__/Fsst.test.ts.snap index 5f2f9de..d2ebbe9 100644 --- a/src/extractor/__snapshots__/Fsst.test.ts.snap +++ b/src/extractor/__snapshots__/Fsst.test.ts.snap @@ -6,39 +6,33 @@ exports[`Fsst Blood & Sinners 1`] = ` "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 360, "title": "Blood.and.Sinners.2025.mkv", }, - "sourceId": "fsst_de_0", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/16/a135ab46b1243e41c0282c16d7cdcbcdf0faf7ee79/900000/900576/900576_360p.mp4/", }, { "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 720, "title": "Blood.and.Sinners.2025.mkv", }, - "sourceId": "fsst_de_1", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/16/801a42b7ac11f7e08101fe8c3cb537045ddf5c9b19/900000/900576/900576_720p.mp4/", }, { "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 1080, "title": "Blood.and.Sinners.2025.mkv", }, - "sourceId": "fsst_de_2", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/16/fae180e05d8d7af50fff44c7a24232b5c58023d7f5/900000/900576/900576.mp4/", }, ] @@ -50,39 +44,33 @@ exports[`Fsst Dead City 1`] = ` "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 360, "title": "the.walking.dead.dead.city.s02e06.german.dl.1080p.web.x264-wvf.mkv", }, - "sourceId": "fsst_de_0", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/9/99510c9abde68b5e82b08cd2391724050c8e47cc86/901000/901994/901994_360p.mp4/", }, { "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 720, "title": "the.walking.dead.dead.city.s02e06.german.dl.1080p.web.x264-wvf.mkv", }, - "sourceId": "fsst_de_1", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/9/868ae0229e543e7a9a6f7cf4b3df88f45a49aa11fa/901000/901994/901994_720p.mp4/", }, { "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 1080, "title": "the.walking.dead.dead.city.s02e06.german.dl.1080p.web.x264-wvf.mkv", }, - "sourceId": "fsst_de_2", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/9/44ff1bf6024db32840054f00029afb1164408b3c1f/901000/901994/901994.mp4/", }, ] @@ -94,13 +82,11 @@ exports[`Fsst How to Train Your Dragon 1`] = ` "format": "mp4", "label": "Fsst", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 360, "title": "How To Train Your Dragon 2025.mp4", }, - "sourceId": "fsst_de_0", + "sourceId": "fsst_", "url": "https://www.secvideo1.online/get_file/10/648fc9b266670e9ad6756a066a68a8db0b36868457/902000/902668/902668_360p.mp4/", }, ] diff --git a/src/extractor/__snapshots__/KinoGer.test.ts.snap b/src/extractor/__snapshots__/KinoGer.test.ts.snap index 6e7811c..7ebc032 100644 --- a/src/extractor/__snapshots__/KinoGer.test.ts.snap +++ b/src/extractor/__snapshots__/KinoGer.test.ts.snap @@ -6,16 +6,14 @@ exports[`KinoGer Blood & Sinners 1`] = ` "format": "hls", "label": "KinoGer", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 1080, "title": "Blood.and.Sinners.2025.mkv", }, "requestHeaders": { "Referer": "https://kinoger.re", }, - "sourceId": "kinoger_de", + "sourceId": "kinoger_", "ttl": 21600000, "url": "https://scvo.processorprediction.shop/v4/bgm/ge5fhb/cf-master.txt?v=1748766126", }, @@ -28,16 +26,14 @@ exports[`KinoGer Dead City 1`] = ` "format": "hls", "label": "KinoGer", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 1080, "title": "the.walking.dead.dead.city.s02e05.german.dl.1080p.web.x264-wvf.mkv", }, "requestHeaders": { "Referer": "https://kinoger.re", }, - "sourceId": "kinoger_de", + "sourceId": "kinoger_", "ttl": 21600000, "url": "https://sipt.responseautomation.cyou/v4/vz1/x6tsx9/cf-master.txt?v=1748957184", }, diff --git a/src/extractor/__snapshots__/Mixdrop.test.ts.snap b/src/extractor/__snapshots__/Mixdrop.test.ts.snap index 5bff5d4..5d3d824 100644 --- a/src/extractor/__snapshots__/Mixdrop.test.ts.snap +++ b/src/extractor/__snapshots__/Mixdrop.test.ts.snap @@ -9,12 +9,10 @@ exports[`Mixdrop mixdrop.my /e/ 1`] = ` "label": "Mixdrop (via MediaFlow Proxy)", "meta": { "bytes": 1059806248, - "countryCodes": [ - "de", - ], + "countryCodes": [], "title": "28_giorni_dopo_2002_HD_-_Altadefinizione01.mp4", }, - "sourceId": "mixdrop_de", + "sourceId": "mixdrop_", "ttl": 900000, "url": "https://mediaflow-proxy.test/extractor/video?host=Mixdrop&api_password=asdfg&d=https%3A%2F%2Fmixdrop.my%2Fe%2Fknq0kj8waq44l8&redirect_stream=true", }, diff --git a/src/extractor/__snapshots__/SaveFiles.test.ts.snap b/src/extractor/__snapshots__/SaveFiles.test.ts.snap index 0ec71f3..86afaad 100644 --- a/src/extractor/__snapshots__/SaveFiles.test.ts.snap +++ b/src/extractor/__snapshots__/SaveFiles.test.ts.snap @@ -6,13 +6,11 @@ exports[`SafeFiles savefiles /d/ 1`] = ` "format": "hls", "label": "SaveFiles", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 532, "title": "dexter resurrection s01e01 1080p web h264-successfulcrab", }, - "sourceId": "savefiles_en", + "sourceId": "savefiles_", "ttl": 21600000, "url": "https://s3.savefiles.com/hls2/01/00133/,c51qm3p0rhtm_n,.urlset/master.m3u8?t=ptFA7PhoGXdNqH2jfu3EVKT4Fn5laaqfHBW1MMOa74o&s=1755775635&e=43200&v=199331&srv=s1&i=0.0&sp=0", }, @@ -25,13 +23,11 @@ exports[`SafeFiles savefiles /e/ 1`] = ` "format": "hls", "label": "SaveFiles", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, "title": "dexter resurrection s01e01 1080p web h264-successfulcrab", }, - "sourceId": "savefiles_en", + "sourceId": "savefiles_", "ttl": 21600000, "url": "https://s3.savefiles.com/hls2/01/00133/,j4orffalv89e_n,.urlset/master.m3u8?t=uwZgBnJBJ-wfgWvDwmLamHkR-vuqzrBFuLH7QoAH-QE&s=1755775635&e=43200&v=199330&srv=s1&i=0.0&sp=0", }, @@ -44,13 +40,11 @@ exports[`SafeFiles savefiles 1`] = ` "format": "hls", "label": "SaveFiles", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, "title": "King Of The Hill S14E01 1080p HEVC x265-MeGusta", }, - "sourceId": "savefiles_en", + "sourceId": "savefiles_", "ttl": 21600000, "url": "https://s3.savefiles.com/hls2/01/00153/,ip0k0dj2g0i3_n,lang/eng/ip0k0dj2g0i3_eng,.urlset/master.m3u8?t=Emm2ipTW9Db4fBNcQqc8qNju3Yysg0GjGoYiC0FB-G4&s=1755078897&e=43200&v=406428&srv=s1&i=0.0&sp=0&fr=ip0k0dj2g0i3", }, @@ -65,13 +59,11 @@ exports[`SafeFiles streamhls /e/ 1`] = ` "format": "hls", "label": "SaveFiles", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, "title": "King Of The Hill S14E01 1080p HEVC x265-MeGusta", }, - "sourceId": "savefiles_en", + "sourceId": "savefiles_", "ttl": 21600000, "url": "https://s3.savefiles.com/hls2/01/00153/,ip0k0dj2g0i3_n,lang/eng/ip0k0dj2g0i3_eng,.urlset/master.m3u8?t=Emm2ipTW9Db4fBNcQqc8qNju3Yysg0GjGoYiC0FB-G4&s=1755078897&e=43200&v=406428&srv=s1&i=0.0&sp=0&fr=ip0k0dj2g0i3", }, diff --git a/src/extractor/__snapshots__/Soaper.test.ts.snap b/src/extractor/__snapshots__/Soaper.test.ts.snap index 55e9a1a..165d8d6 100644 --- a/src/extractor/__snapshots__/Soaper.test.ts.snap +++ b/src/extractor/__snapshots__/Soaper.test.ts.snap @@ -6,13 +6,10 @@ exports[`Soaper Black Mirror 1`] = ` "format": "hls", "label": "Soaper", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, - "title": "Black Mirror 4x2", }, - "sourceId": "soaper_en", + "sourceId": "soaper_", "ttl": 43200000, "url": "https://soaper.live/home/index/TVM3U8?key=RnX8mnL4l5hEz38dxBAbFjV27jbe3Yh13LqxV48ViM7j60OLjmh3w5PJ8nQPh2brz0eEAAt50Zvv7d89hpM3OLWQ6nsjb4n0l3ApSna13LB3EbUxbbyJ70KwuVjaooMP5mIE4MbjyvqJHjRq3amYA5SPV8NRl8wmivl8YyrlMmFx6VomOAp2C5WR46w10v.m3u8", }, @@ -25,13 +22,10 @@ exports[`Soaper Full Metal Jacket 1`] = ` "format": "hls", "label": "Soaper", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 1080, - "title": "Full Metal Jacket (1987)", }, - "sourceId": "soaper_en", + "sourceId": "soaper_", "ttl": 43200000, "url": "https://soaper.live/home/index/M3U8?key=RnX8BOr9NRiEz3W5ownqUjVQ3LQew9S13L8VbnEQCM72wz1AEyIrld1z4NXwF2ByZneoX7i5Ez9BlPpxUpMeqJ2qdOhjb4PlArnASnlEpp2b8Whx1lwpx4oRIVzEKX0Z6rcEQWArMlxvTjRq3aMEnQUP3mvva8RZfvldjM9mz5FxZZv6QKdzcVxqX8q.m3u8", }, @@ -44,13 +38,10 @@ exports[`Soaper last of us s2e3 1`] = ` "format": "hls", "label": "Soaper", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": undefined, - "title": "The Last of Us 2x3", }, - "sourceId": "soaper_de", + "sourceId": "soaper_", "ttl": 43200000, "url": "https://soaper.live/home/index/TVM3U8?key=LrezaYpLOxT4O61m8e3mSLdBzLNyRaf8zPmERxvwh1Mrj6zbVEUvAzRv2KZLu5PVvRKqwbcz6qmb9vbOi3wQZ4ae8RsZ9N3Jm9Yatw4yE55bBp.m3u8", }, diff --git a/src/extractor/__snapshots__/StreamEmbed.test.ts.snap b/src/extractor/__snapshots__/StreamEmbed.test.ts.snap index 7c71ec3..e2ab8a4 100644 --- a/src/extractor/__snapshots__/StreamEmbed.test.ts.snap +++ b/src/extractor/__snapshots__/StreamEmbed.test.ts.snap @@ -6,13 +6,11 @@ exports[`StreamEmbed watch.gxplayer.xyz 1`] = ` "format": "hls", "label": "StreamEmbed", "meta": { - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 720, "title": "Baymax - Riesiges Robowabohu.mp4", }, - "sourceId": "streamembed_de", + "sourceId": "streamembed_", "ttl": 900000, "url": "https://watch.gxplayer.xyz/m3u8/3/30b4a92517ace5825f5944c8a794ad3e/master.txt?s=1&id=11782&cache=1", }, diff --git a/src/extractor/__snapshots__/Streamtape.test.ts.snap b/src/extractor/__snapshots__/Streamtape.test.ts.snap index 96e3229..0fe57f6 100644 --- a/src/extractor/__snapshots__/Streamtape.test.ts.snap +++ b/src/extractor/__snapshots__/Streamtape.test.ts.snap @@ -7,12 +7,10 @@ exports[`Streamtape streamtape.com /e 1`] = ` "label": "Streamtape (via MediaFlow Proxy)", "meta": { "bytes": 1187340184, - "countryCodes": [ - "es", - ], + "countryCodes": [], "title": "mickey-17-2025-[latino].mp4", }, - "sourceId": "streamtape_es", + "sourceId": "streamtape_", "ttl": 900000, "url": "https://mediaflow.test.org/extractor/video?host=Streamtape&api_password=test&d=https%3A%2F%2Fstreamtape.com%2Fe%2F84Kb3mkoYAiokmW&redirect_stream=true", }, diff --git a/src/extractor/__snapshots__/SuperVideo.test.ts.snap b/src/extractor/__snapshots__/SuperVideo.test.ts.snap index 30c1c13..6027b7f 100644 --- a/src/extractor/__snapshots__/SuperVideo.test.ts.snap +++ b/src/extractor/__snapshots__/SuperVideo.test.ts.snap @@ -8,13 +8,11 @@ exports[`SuperVideo embed only 1`] = ` "format": "hls", "label": "SuperVideo", "meta": { - "countryCodes": [ - "it", - ], + "countryCodes": [], "height": 1080, "title": "", }, - "sourceId": "supervideo_it", + "sourceId": "supervideo_", "ttl": 10800000, "url": "https://hfs308.serversicuro.cc/hls/,dnzpe7rf5xg4a3gyva3h5orlyw23xosfzwunl5xbmjpz2l6t3doh5zbwynna,.urlset/master.m3u8", }, @@ -30,13 +28,11 @@ exports[`SuperVideo supervideo.cc /e/ 1`] = ` "label": "SuperVideo", "meta": { "bytes": 1073741824, - "countryCodes": [ - "de", - ], + "countryCodes": [], "height": 720, "title": "des-teufels-bad-2024", }, - "sourceId": "supervideo_de", + "sourceId": "supervideo_", "ttl": 10800000, "url": "https://hfs309.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p577fze64cp3rsmdyq,.urlset/master.m3u8", }, @@ -50,13 +46,11 @@ exports[`SuperVideo supervideo.tv /embed-/ 1`] = ` "label": "SuperVideo", "meta": { "bytes": 219571814, - "countryCodes": [ - "it", - ], + "countryCodes": [], "height": 344, "title": "Babylon 5 2x3", }, - "sourceId": "supervideo_it", + "sourceId": "supervideo_", "ttl": 10800000, "url": "https://hfs307.serversicuro.cc/hls/,dnzpaalv5xg4a3gyvavh52jryoft7o5cd5nm7cwtnu74kmc4ohiklh5qwq4q,.urlset/master.m3u8", }, diff --git a/src/extractor/__snapshots__/Uqload.test.ts.snap b/src/extractor/__snapshots__/Uqload.test.ts.snap index a4a4e7f..76a6283 100644 --- a/src/extractor/__snapshots__/Uqload.test.ts.snap +++ b/src/extractor/__snapshots__/Uqload.test.ts.snap @@ -7,13 +7,11 @@ exports[`Uqload uqload.net /embed- 1`] = ` "label": "Uqload (via MediaFlow Proxy)", "meta": { "bytes": 431690639, - "countryCodes": [ - "fr", - ], + "countryCodes": [], "height": 360, "title": "Black Mirror S07E06 MULTI VFF 1080p WEBrip EAC3 5 1 x265-TyHD", }, - "sourceId": "uqload_fr", + "sourceId": "uqload_", "ttl": 900000, "url": "https://mediaflow.test.org/extractor/video?host=Uqload&api_password=test&d=https%3A%2F%2Fuqload.net%2Fz0xbr87oz637.html&redirect_stream=true", }, diff --git a/src/extractor/__snapshots__/VidSrc.test.ts.snap b/src/extractor/__snapshots__/VidSrc.test.ts.snap index 3546e65..607c242 100644 --- a/src/extractor/__snapshots__/VidSrc.test.ts.snap +++ b/src/extractor/__snapshots__/VidSrc.test.ts.snap @@ -6,13 +6,11 @@ exports[`VidSrc Black Mirror 1`] = ` "format": "hls", "label": "VidSrc (CloudStream Pro)", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 1040, "title": "Black Mirror (2011) S04E02", }, - "sourceId": "vidsrc_CloudStream-Pro_en", + "sourceId": "vidsrc_CloudStream-Pro_", "ttl": 10800000, "url": "https://tmstr5.shadowlandschronicles.com/pl/H4sIAAAAAAAAAxXK226DIAAA0F8CrGvd27oCq1UcyP1NQdtUaozptsyvX3aez9DBQxgjgBmAPeqK_Yh2eTeG4tAPMCDwysHNaeDbevKVMM.XgMWps2XtTWGkLR8M.feQ2KroeeXG_db4vCq82.T8lkerZ0OTlLLsWuA2lnwT1FK7e6r0B5nEHL_4pNAgme2sJj28_kT0f0kuya0Sj6W6wONRnkrpAHP9vZwEzRE3T8JOohmwtwMlTimNeiw2BQWRsM4CKZ3PFuUyT1usLy6LWb3F1s3p0hPRSpqqxibcPpaG3.Mq5qS4vn4bqm.NTp8KFzOfmBykWP4AirgCPSEBAAA-/master.m3u8", }, @@ -25,13 +23,11 @@ exports[`VidSrc Full Metal Jacket 1`] = ` "format": "hls", "label": "VidSrc (CloudStream Pro)", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 714, "title": "Full Metal Jacket (1987)", }, - "sourceId": "vidsrc_CloudStream-Pro_en", + "sourceId": "vidsrc_CloudStream-Pro_", "ttl": 10800000, "url": "https://tmstr4.shadowlandschronicles.com/pl/H4sIAAAAAAAAAwXB7XqCIBgA0FtCsa_97IMcFSa.gPBPwc1H0Fm5Wl79zllVS4eRdZv10uJkuarrJF7Yr8at1lWyRvZDC7cFP3IT2YU4PHAmiGKB59C1Z41bbUQ7czDQ.AgpMv6csHtqNTFGzMMGessV_bOSvRkadab4riBhWx0nxuPxDtFnXMSjqAOdM5AtK3lyjjxiyD.LYAaRbqvC8_MF_L0Bsq8GeuGHxUlLU9rSZapvkSTyUfsNkcEJi8eb9jRpRPJkMTO6H4mV33cnaCqP_LeIpw46iVlHrxDp2c6G5ILtbeoC9I42itxy_3plqev50N5FLDHfTS94T1AQKXUZrmwOA1dtVIFFOaDNP7JZ1PhBAQAA/master.m3u8", }, @@ -47,12 +43,6 @@ exports[`VidSrc rate limit issues are retried and fail if no tlds are left 1`] = "format": "unknown", "isExternal": true, "label": "VidSrc", - "meta": { - "countryCodes": [ - "en", - ], - "title": "Dexter: Resurrection 1x1", - }, "sourceId": "vidsrc", "url": "https://vidsrc.xyz/embed/movie/tt33043892/1/1", }, diff --git a/src/extractor/__snapshots__/VixSrc.test.ts.snap b/src/extractor/__snapshots__/VixSrc.test.ts.snap index 7583a38..cd8f33f 100644 --- a/src/extractor/__snapshots__/VixSrc.test.ts.snap +++ b/src/extractor/__snapshots__/VixSrc.test.ts.snap @@ -14,7 +14,7 @@ exports[`VixSrc Black Mirror 1`] = ` "height": 1080, "title": "Black.Mirror.S04E02.Arkangel.1080p.NF.WEB-DL.ITA.ENG.DDP5.1.H.264.mkv", }, - "sourceId": "vixsrc_it", + "sourceId": "vixsrc_", "ttl": 900000, "url": "https://mediaflow-proxy.test/proxy/hls/manifest.m3u8?api_password=asdfg&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fvixsrc.to%2Ftv%2F42009%2F4%2F2&d=https%3A%2F%2Fvixsrc.to%2Fplaylist%2F307316%3Ftoken%3D2f5004a3b15f4ccdb3446f2b132a0957%26expires%3D1755949828%26h%3D1", }, @@ -37,7 +37,7 @@ exports[`VixSrc Full Metal Jacket 1`] = ` "height": 1080, "title": "Full.metal.jacket.1987.1080p.x264.AC3.ITA.AAC.ENG.Subs.mkv", }, - "sourceId": "vixsrc_it", + "sourceId": "vixsrc_", "ttl": 900000, "url": "https://mediaflow-proxy.test/proxy/hls/manifest.m3u8?api_password=asdfg&h_user-agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F136.0.0.0+Safari%2F537.36&h_referer=https%3A%2F%2Fvixsrc.to%2Fmovie%2F600&d=https%3A%2F%2Fvixsrc.to%2Fplaylist%2F219736%3Ftoken%3D689cf5587c2fec67fd83503a2a7dfd64%26expires%3D1755949827%26h%3D1", }, diff --git a/src/extractor/__snapshots__/XPrime.test.ts.snap b/src/extractor/__snapshots__/XPrime.test.ts.snap index 4cfa5f9..5aa7780 100644 --- a/src/extractor/__snapshots__/XPrime.test.ts.snap +++ b/src/extractor/__snapshots__/XPrime.test.ts.snap @@ -7,16 +7,14 @@ exports[`XPrime primebox Alien: Earth 1`] = ` "label": "XPrime Primebox", "meta": { "bytes": 871281904, - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 1080, "title": "Alien: Earth 1x1 (2025)", }, "requestHeaders": { "Referer": "https://xprime.tv", }, - "sourceId": "xprime_en_primebox", + "sourceId": "xprime_primebox_", "ttl": 21600000, "url": "https://oca.flutch09.workers.dev/?v=cenBDs9Hbnv6PuXYzNUtVR2ZZ2YtmVtQIt6JqHB1dsbz2Y%2BXC9UwPurnE%2BQNk27Yr1WeYeLOOazAnSL8cU0DvVfg4pwKsjHKWIJICiVlvv9ndXII0bR7eaIi%2B11NbAj72pFFryHZjO6MkMKEJJnF4G%2BH1BXIO1ALds7J1A7gj2vc7DbUWL8kPxwH1rMw7NAys20M5s3PsbhgSrMpguZSBm8X&headers=%7B%22origin%22%3A%22https%3A%2F%2Fmoviebox.ng%22%2C%22referer%22%3A%22https%3A%2F%2Fmoviebox.ng%22%7D&safe", }, @@ -25,16 +23,14 @@ exports[`XPrime primebox Alien: Earth 1`] = ` "label": "XPrime Primebox", "meta": { "bytes": 420170402, - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 480, "title": "Alien: Earth 1x1 (2025)", }, "requestHeaders": { "Referer": "https://xprime.tv", }, - "sourceId": "xprime_en_primebox", + "sourceId": "xprime_primebox_", "ttl": 21600000, "url": "https://oca.flutch09.workers.dev/?v=oS2n5QbXtpa%2BWXMpIIi0SqeqadEyYEAg6AmHvpTa3jzooh6zxINciUJrR%2B6b1xLZtxbnbAM3mrGGXOa5yckYkNSC%2BKGEFSdofXw298vq3ssHpI9LBN6XLvhtxnDfmub0QoMG%2Fkvgjhb8T%2FwiCHo9NeSX%2FetW6ANppW9jhZn0Hv3hsWdmES1jqj6nfIjQ1ur53htjr2vF7mZbZSsJ&headers=%7B%22origin%22%3A%22https%3A%2F%2Fmoviebox.ng%22%2C%22referer%22%3A%22https%3A%2F%2Fmoviebox.ng%22%7D&safe", }, @@ -48,16 +44,14 @@ exports[`XPrime primebox Superman 1`] = ` "label": "XPrime Primebox", "meta": { "bytes": 401394599, - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 480, "title": "Superman", }, "requestHeaders": { "Referer": "https://xprime.tv", }, - "sourceId": "xprime_en_primebox", + "sourceId": "xprime_primebox_", "ttl": 21600000, "url": "https://oca.flutch09.workers.dev/?v=3RVZDNxcKNs6%2BqcdL1bDCgq9Yp1iPYqb6a9yToLq66vlzu08R1Hz7jiKx4OBH6YzJBcK7skuiDGDwIpro78ReVjMLtMHCfa0fBo0aJ9RYULOUMkLrZxjzSTXvo5Jss1Jiss8e9Z8pFdlC1VqwAUm6G9mx0u41dFLJ0GTJrwCesPe4zflbL8I76WkWYKBMT2MwHvDTIO4r3V0nf8BsRswsNJZ&headers=%7B%22origin%22%3A%22https%3A%2F%2Fmoviebox.ng%22%2C%22referer%22%3A%22https%3A%2F%2Fmoviebox.ng%22%7D&safe", }, @@ -66,16 +60,14 @@ exports[`XPrime primebox Superman 1`] = ` "label": "XPrime Primebox", "meta": { "bytes": 896638128, - "countryCodes": [ - "en", - ], + "countryCodes": [], "height": 720, "title": "Superman", }, "requestHeaders": { "Referer": "https://xprime.tv", }, - "sourceId": "xprime_en_primebox", + "sourceId": "xprime_primebox_", "ttl": 21600000, "url": "https://oca.flutch09.workers.dev/?v=gUbhHOwpFIqajH6NnoH%2BYE9AWORUUogamkdYQedBfKi5eQtyniWtxmI1%2B5Swyl1ThC6vM0qHl53s0Q7j%2BDaWFhVRJQL%2BJRxbuOL1iCz1w5xVPLDU%2FTJ3zhf9EbULPGbbee14kdNYNc5%2FlF2sNe6RiUupsvhlj%2BjcgsT8uypX%2BudVyw1jVEUVogufPnc0lSrfoE98IZlOgdjBZKE3%2Fcw3erD2&headers=%7B%22origin%22%3A%22https%3A%2F%2Fmoviebox.ng%22%2C%22referer%22%3A%22https%3A%2F%2Fmoviebox.ng%22%7D&safe", }, diff --git a/src/extractor/__snapshots__/YouTube.test.ts.snap b/src/extractor/__snapshots__/YouTube.test.ts.snap index 3cd0e42..88ff8bf 100644 --- a/src/extractor/__snapshots__/YouTube.test.ts.snap +++ b/src/extractor/__snapshots__/YouTube.test.ts.snap @@ -6,12 +6,10 @@ exports[`YouTube Solaris 1`] = ` "format": "unknown", "label": "YouTube", "meta": { - "countryCodes": [ - "en", - ], + "countryCodes": [], "title": "Solaris | SCIENCE FICTION | FULL MOVIE | directed by Tarkovsky", }, - "sourceId": "youtube_en", + "sourceId": "youtube_", "ttl": 21600000, "url": "https://www.youtube.com/watch?v=Z8ZhQPaw4rE", "ytId": "Z8ZhQPaw4rE", diff --git a/src/source/CineHDPlus.ts b/src/source/CineHDPlus.ts index 87adf0b..da117d0 100644 --- a/src/source/CineHDPlus.ts +++ b/src/source/CineHDPlus.ts @@ -35,9 +35,9 @@ export class CineHDPlus extends Source { const $ = cheerio.load(html); - const countryCode: CountryCode = ($('.details__langs').html() as string).includes('Latino') ? CountryCode.mx : CountryCode.es; + const countryCodes = [($('.details__langs').html() as string).includes('Latino') ? CountryCode.mx : CountryCode.es]; - const title = $('meta[property="og:title"]').attr('content') as string; + const title = `${($('meta[property="og:title"]').attr('content') as string).trim()} ${imdbId.season}x${imdbId.episode}`; return Promise.all( $(`[data-num="${imdbId.season}x${imdbId.episode}"]`) @@ -46,7 +46,7 @@ export class CineHDPlus extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/cinehdplus/)) - .map(url => ({ countryCode, referer: seriesPageUrl, title: `${title.trim()} ${imdbId.season}x${imdbId.episode}`, url })), + .map(url => ({ url, meta: { countryCodes, title } })), ); }; diff --git a/src/source/Cuevana.ts b/src/source/Cuevana.ts index 1539a05..6f7f8f4 100644 --- a/src/source/Cuevana.ts +++ b/src/source/Cuevana.ts @@ -60,27 +60,27 @@ export class Cuevana extends Source { if (elText.includes('Latino')) { return $('[data-tr], [data-video]', el) - .map((_i, el) => ({ countryCode: CountryCode.mx, title, url: new URL($(el).attr('data-tr') ?? $(el).attr('data-video') as string) })) + .map((_i, el) => ({ url: new URL($(el).attr('data-tr') ?? $(el).attr('data-video') as string), meta: { countryCodes: [CountryCode.mx], title } })) .toArray(); } return $('[data-tr], [data-video]', el) - .map((_i, el) => ({ countryCode: CountryCode.es, title, url: new URL($(el).attr('data-tr') ?? $(el).attr('data-video') as string) })) + .map((_i, el) => ({ url: new URL($(el).attr('data-tr') ?? $(el).attr('data-video') as string), meta: { countryCodes: [CountryCode.es], title } })) .toArray(); }) .toArray(); return Promise.all( - urlResults.map(async (urlResult) => { - if (!urlResult.url.host.includes('cuevana3')) { - return urlResult; + urlResults.map(async ({ url, meta }) => { + if (!url.host.includes('cuevana3')) { + return { url, meta }; } - const html = await this.fetcher.text(ctx, urlResult.url, { headers: { Referer: pageUrl.origin } }); + const html = await this.fetcher.text(ctx, url, { headers: { Referer: pageUrl.origin } }); const urlMatcher = html.match(/url ?= ?'(.*)'/) as string[]; - return { ...urlResult, url: new URL(urlMatcher[1] as string) }; + return { url: new URL(urlMatcher[1] as string), meta }; }), ); }; diff --git a/src/source/Einschalten.ts b/src/source/Einschalten.ts index 636b78a..2e2f960 100644 --- a/src/source/Einschalten.ts +++ b/src/source/Einschalten.ts @@ -30,8 +30,8 @@ export class Einschalten extends Source { public async handleInternal(ctx: Context, _type: string, id: Id): Promise { const tmdbId = await getTmdbId(ctx, this.fetcher, id); - const { releaseName, streamUrl } = JSON.parse(await this.fetcher.text(ctx, new URL(`/api/movies/${tmdbId.id}/watch`, this.baseUrl))) as EinschaltenResponse; + const { releaseName: title, streamUrl } = JSON.parse(await this.fetcher.text(ctx, new URL(`/api/movies/${tmdbId.id}/watch`, this.baseUrl))) as EinschaltenResponse; - return [{ countryCode: CountryCode.de, title: releaseName, url: new URL(streamUrl) }]; + return [{ url: new URL(streamUrl), meta: { countryCodes: [CountryCode.de], title } }]; }; } diff --git a/src/source/Eurostreaming.ts b/src/source/Eurostreaming.ts index 55e7a34..b2f39dd 100644 --- a/src/source/Eurostreaming.ts +++ b/src/source/Eurostreaming.ts @@ -45,7 +45,7 @@ export class Eurostreaming extends Source { .map((_i, el) => new URL($(el).attr('data-link') as string)) .toArray() .filter(url => !url.host.match(/eurostreaming/)) - .map(url => ({ countryCode: CountryCode.it, title, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.it], title } })), ); }; diff --git a/src/source/Frembed.ts b/src/source/Frembed.ts index 354e0ad..9768bd6 100644 --- a/src/source/Frembed.ts +++ b/src/source/Frembed.ts @@ -46,6 +46,6 @@ export class Frembed extends Source { ? `${json['title']} ${tmdbId.season}x${tmdbId.episode}` : json['title']; - return urls.map(url => ({ countryCode: CountryCode.fr, title, url })); + return urls.map(url => ({ url, meta: { countryCodes: [CountryCode.fr], title } })); }; } diff --git a/src/source/FrenchCloud.ts b/src/source/FrenchCloud.ts index 7ff9adb..5e64236 100644 --- a/src/source/FrenchCloud.ts +++ b/src/source/FrenchCloud.ts @@ -36,7 +36,7 @@ export class FrenchCloud extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/frenchcloud/)) - .map(url => ({ countryCode: CountryCode.fr, referer: pageUrl, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.fr] } })), ); }; } diff --git a/src/source/HomeCine.ts b/src/source/HomeCine.ts index 3a92218..1978978 100644 --- a/src/source/HomeCine.ts +++ b/src/source/HomeCine.ts @@ -51,16 +51,16 @@ export class HomeCine extends Source { return $('.les-content a') .map((_i, el) => { - let countryCode: CountryCode; + let countryCodes: CountryCode[]; if ($(el).text().toLowerCase().includes('latino')) { - countryCode = CountryCode.mx; + countryCodes = [CountryCode.mx]; } else if ($(el).text().toLowerCase().includes('castellano')) { - countryCode = CountryCode.es; + countryCodes = [CountryCode.es]; } else { return []; } - return { countryCode, title, url: new URL($('iframe', $(el).attr('href')).attr('src') as string) }; + return { url: new URL($('iframe', $(el).attr('href')).attr('src') as string), meta: { countryCodes, title } }; }).toArray(); }; diff --git a/src/source/KinoGer.ts b/src/source/KinoGer.ts index c71bb07..d30789f 100644 --- a/src/source/KinoGer.ts +++ b/src/source/KinoGer.ts @@ -42,7 +42,7 @@ export class KinoGer extends Source { return Array.from(html.matchAll(/\.show\(.*/g)) .map(showJsMatch => this.findEpisodeUrlInShowJs(showJsMatch[0], seasonIndex, episodeIndex)) .filter((url): url is URL => url !== undefined && !['kinoger.be', 'kinoger.ru'].includes(url.host)) - .map(url => ({ countryCode: CountryCode.de, title, url })); + .map(url => ({ url, meta: { countryCodes: [CountryCode.de], title } })); }; private readonly findEpisodeUrlInShowJs = (showJs: string, seasonIndex: number, episodeIndex: number): URL | undefined => { diff --git a/src/source/MegaKino.ts b/src/source/MegaKino.ts index 51e85bb..87c4ae6 100644 --- a/src/source/MegaKino.ts +++ b/src/source/MegaKino.ts @@ -41,7 +41,7 @@ export class MegaKino extends Source { $(`.video-inside iframe`) .map((_i, el) => new URL(($(el).attr('data-src') ?? $(el).attr('src')) as string)) .toArray() - .map(url => ({ countryCode: CountryCode.de, referer: pageUrl, title, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.de], title } })), ); }; diff --git a/src/source/MeineCloud.ts b/src/source/MeineCloud.ts index 3f14c10..381cd75 100644 --- a/src/source/MeineCloud.ts +++ b/src/source/MeineCloud.ts @@ -36,7 +36,7 @@ export class MeineCloud extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/meinecloud/)) - .map(url => ({ countryCode: CountryCode.de, referer: pageUrl, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.de] } })), ); }; } diff --git a/src/source/MostraGuarda.ts b/src/source/MostraGuarda.ts index 4d55ef6..2bb502c 100644 --- a/src/source/MostraGuarda.ts +++ b/src/source/MostraGuarda.ts @@ -36,7 +36,7 @@ export class MostraGuarda extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/mostraguarda/)) - .map(url => ({ countryCode: CountryCode.it, referer: pageUrl, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.it] } })), ); }; } diff --git a/src/source/Movix.ts b/src/source/Movix.ts index eb55380..ce1ae7e 100644 --- a/src/source/Movix.ts +++ b/src/source/Movix.ts @@ -46,6 +46,6 @@ export class Movix extends Source { ? `${json['tmdb_details']['title']} ${tmdbId.season}x${tmdbId.episode}` : json['tmdb_details']['title']; - return urls.map(url => ({ countryCode: CountryCode.fr, title, url })); + return urls.map(url => ({ url, meta: { countryCodes: [CountryCode.fr], title } })); }; } diff --git a/src/source/PrimeWire.ts b/src/source/PrimeWire.ts index 2df1c9a..5a6f5ea 100644 --- a/src/source/PrimeWire.ts +++ b/src/source/PrimeWire.ts @@ -85,7 +85,7 @@ export class PrimeWire extends Source { await this.redirectUrlCache.set(redirectUrl.href, targetUrlHref); } - return { countryCode: CountryCode.en, url: new URL(targetUrlHref) }; + return { url: new URL(targetUrlHref), meta: { countryCodes: [CountryCode.en] } }; }), ); }; diff --git a/src/source/Soaper.ts b/src/source/Soaper.ts index a60d021..d4a0587 100644 --- a/src/source/Soaper.ts +++ b/src/source/Soaper.ts @@ -47,12 +47,12 @@ export class Soaper extends Source { const episodeUrl = new URL(episodePageHref, this.baseUrl); const title = `${name} ${tmdbId.season}x${tmdbId.episode}`; - return [{ countryCode: CountryCode.en, title, url: episodeUrl }]; + return [{ url: episodeUrl, meta: { countryCodes: [CountryCode.en], title } }]; } const title = `${name} (${year})`; - return [{ countryCode: CountryCode.en, title, url: pageUrl }]; + return [{ url: pageUrl, meta: { countryCodes: [CountryCode.en], title } }]; }; private readonly fetchPageUrl = async (ctx: Context, keyword: string, year: number, hrefPrefix: string): Promise => { diff --git a/src/source/Source.ts b/src/source/Source.ts index 5ddd4fd..49d31bb 100644 --- a/src/source/Source.ts +++ b/src/source/Source.ts @@ -3,13 +3,12 @@ import KeyvSqlite from '@keyv/sqlite'; import { Cacheable, CacheableMemory, Keyv } from 'cacheable'; import { ContentType } from 'stremio-addon-sdk'; import { NotFoundError } from '../error'; -import { Context, CountryCode } from '../types'; +import { Context, CountryCode, Meta } from '../types'; import { getCacheDir, Id } from '../utils'; export interface SourceResult { - countryCode: CountryCode; - title?: string; url: URL; + meta: Meta; } export abstract class Source { @@ -27,7 +26,7 @@ export abstract class Source { private static readonly sourceResultCache = new Cacheable({ primary: new Keyv({ store: new CacheableMemory({ lruSize: 1024 }) }), - secondary: new Keyv(new KeyvSqlite(`sqlite://${getCacheDir()}/webstreamr-source-cache.sqlite`)), + secondary: new Keyv(new KeyvSqlite(`sqlite://${getCacheDir()}/webstreamr-source-cache-v2.sqlite`)), stats: true, }); @@ -59,6 +58,6 @@ export abstract class Source { await Source.sourceResultCache.set(cacheKey, sourceResults, this.ttl); } - return sourceResults.filter(sourceResult => sourceResult.countryCode in ctx.config); + return sourceResults.filter(sourceResult => sourceResult.meta.countryCodes?.some(countryCode => countryCode in ctx.config)); } } diff --git a/src/source/StreamKiste.ts b/src/source/StreamKiste.ts index 55d28c6..18c41c2 100644 --- a/src/source/StreamKiste.ts +++ b/src/source/StreamKiste.ts @@ -35,7 +35,7 @@ export class StreamKiste extends Source { const $ = cheerio.load(html); - const title = $('meta[property="og:title"]').attr('content') as string; + const title = `${($('meta[property="og:title"]').attr('content') as string).trim()} ${imdbId.season}x${imdbId.episode}`; return Promise.all( $(`[data-num="${imdbId.season}x${imdbId.episode}"]`) @@ -44,7 +44,7 @@ export class StreamKiste extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/streamkiste/)) - .map(url => ({ countryCode: CountryCode.de, referer: seriesPageUrl, title: `${title.trim()} ${imdbId.season}x${imdbId.episode}`, url })), + .map(url => ({ url, meta: { countryCodes: [CountryCode.de], title } })), ); }; diff --git a/src/source/VerHdLink.ts b/src/source/VerHdLink.ts index 3204e7d..4aa296f 100644 --- a/src/source/VerHdLink.ts +++ b/src/source/VerHdLink.ts @@ -33,11 +33,11 @@ export class VerHdLink extends Source { return $('._player-mirrors') .map((_i, el) => { - let countryCode: CountryCode; + let countryCodes: CountryCode[]; if ($(el).hasClass('latino')) { - countryCode = CountryCode.mx; + countryCodes = [CountryCode.mx]; } else if ($(el).hasClass('castellano')) { - countryCode = CountryCode.es; + countryCodes = [CountryCode.es]; } else { return []; } @@ -46,7 +46,7 @@ export class VerHdLink extends Source { .map((_i, el) => new URL(($(el).attr('data-link') as string).replace(/^(https:)?\/\//, 'https://'))) .toArray() .filter(url => !url.host.match(/verhdlink/)) - .map(url => ({ countryCode, referer: pageUrl, url })); + .map(url => ({ url, meta: { countryCodes } })); }).toArray(); }; } diff --git a/src/source/VidSrc.ts b/src/source/VidSrc.ts index 2b5eb7c..0d9b4b5 100644 --- a/src/source/VidSrc.ts +++ b/src/source/VidSrc.ts @@ -29,6 +29,6 @@ export class VidSrc extends Source { ? new URL(`/embed/tv/${imdbId.id}/${imdbId.season}/${imdbId.episode}`, this.baseUrl) : new URL(`/embed/movie/${imdbId.id}`, this.baseUrl); - return [{ countryCode: CountryCode.en, url }]; + return [{ url, meta: { countryCodes: [CountryCode.en] } }]; }; } diff --git a/src/source/VixSrc.ts b/src/source/VixSrc.ts index 8c498b7..786b0c1 100644 --- a/src/source/VixSrc.ts +++ b/src/source/VixSrc.ts @@ -29,6 +29,6 @@ export class VixSrc extends Source { ? new URL(`/tv/${tmdbId.id}/${tmdbId.season}/${tmdbId.episode}`, this.baseUrl) : new URL(`/movie/${tmdbId.id}`, this.baseUrl); - return [{ countryCode: CountryCode.multi, url }]; + return [{ url, meta: { countryCodes: [CountryCode.multi] } }]; }; } diff --git a/src/source/XPrime.ts b/src/source/XPrime.ts index 51dd166..c41adad 100644 --- a/src/source/XPrime.ts +++ b/src/source/XPrime.ts @@ -32,13 +32,13 @@ export class XPrime extends Source { const urlPrimebox = new URL(`/primebox?name=${encodeURIComponent(name)}&year=${year}&season=${tmdbId.season}&episode=${tmdbId.episode}`, this.baseUrl); - return [{ countryCode: CountryCode.en, title, url: urlPrimebox }]; + return [{ url: urlPrimebox, meta: { countryCodes: [CountryCode.en], title } }]; } const title = `${name} (${year})`; const urlPrimebox = new URL(`/primebox?name=${encodeURIComponent(name)}&year=${year}`, this.baseUrl); - return [{ countryCode: CountryCode.en, title, url: urlPrimebox }]; + return [{ url: urlPrimebox, meta: { countryCodes: [CountryCode.en], title } }]; }; } diff --git a/src/source/__snapshots__/CineHDPlus.test.ts.snap b/src/source/__snapshots__/CineHDPlus.test.ts.snap index af1e461..3ae9b58 100644 --- a/src/source/__snapshots__/CineHDPlus.test.ts.snap +++ b/src/source/__snapshots__/CineHDPlus.test.ts.snap @@ -3,15 +3,21 @@ exports[`CineHDPlus handle imdb babylon 5 s2e3 (es) 1`] = ` [ { - "countryCode": "es", - "referer": "https://cinehdplus.cam/peliculas/19678-babylon-5-ver-online-hd.html", - "title": "Babylon 5 2x3", + "meta": { + "countryCodes": [ + "es", + ], + "title": "Babylon 5 2x3", + }, "url": "https://supervideo.tv/embed-1p0m1fi9mok8.html", }, { - "countryCode": "es", - "referer": "https://cinehdplus.cam/peliculas/19678-babylon-5-ver-online-hd.html", - "title": "Babylon 5 2x3", + "meta": { + "countryCodes": [ + "es", + ], + "title": "Babylon 5 2x3", + }, "url": "https://dropload.io/embed-957hny8mzrvh.html", }, ] @@ -20,15 +26,21 @@ exports[`CineHDPlus handle imdb babylon 5 s2e3 (es) 1`] = ` exports[`CineHDPlus handle imdb black mirror s2e3 (mx) 1`] = ` [ { - "countryCode": "mx", - "referer": "https://cinehdplus.cam/peliculas/9582-black-mirror-ver-online-hd.html", - "title": "Black Mirror 2x3", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "Black Mirror 2x3", + }, "url": "https://supervideo.tv/embed-avmgppo37sx1.html", }, { - "countryCode": "mx", - "referer": "https://cinehdplus.cam/peliculas/9582-black-mirror-ver-online-hd.html", - "title": "Black Mirror 2x3", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "Black Mirror 2x3", + }, "url": "https://dropload.io/embed-t058ulwwkwn6.html", }, ] diff --git a/src/source/__snapshots__/Cuevana.test.ts.snap b/src/source/__snapshots__/Cuevana.test.ts.snap index bd66a1c..39739d0 100644 --- a/src/source/__snapshots__/Cuevana.test.ts.snap +++ b/src/source/__snapshots__/Cuevana.test.ts.snap @@ -3,43 +3,75 @@ exports[`Cuevana does not return es content for mx 1`] = ` [ { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamwish.to/e/9icce2ca0yir", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filemoon.sx/e/fgxxkidqq4jp", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://voe.sx/e/h87m8wyj5x8j", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://doodstream.com/e/ju53ufkqy9hc", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamtape.com/e/WgxgBAwOyGfbZxY", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://waaw.to/f/xtG7M9GL2MmV", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filelions.to/v/tnehp8g7zght", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://plustream.com/embedb2/gHHRwqKdHgF5MYniuxGWnG8j_vIW77uC4a4vrO5iwLktMwS8ryr2i.bhH3SiGHw6PKNBoqxCHLDyc1Pw6EPhwg--", }, ] @@ -48,38 +80,66 @@ exports[`Cuevana does not return es content for mx 1`] = ` exports[`Cuevana does not return mx content for es 1`] = ` [ { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamwish.to/e/mtb09rms1njz", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filemoon.sx/e/r77fu9ez9nsv", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://voe.sx/e/cepsnqy0qgyo", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://doodstream.com/e/ny5qlz61x9xt", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://waaw.to/f/hlHhgFkmWHom", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://plustream.com/embedb2/DpAUEfJrAHkBAdb.6erU25OAypGXqMugDfQuWU2sTDBKHNQmIxzyq1b2Ma06kb7EgL6d4hdzw8quWDimAGnIvQ--", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://vidhidepro.com/v/lhaueh479t2v", }, ] @@ -88,78 +148,138 @@ exports[`Cuevana does not return mx content for es 1`] = ` exports[`Cuevana handle el camino 1`] = ` [ { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamwish.to/e/9icce2ca0yir", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filemoon.sx/e/fgxxkidqq4jp", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://voe.sx/e/h87m8wyj5x8j", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://doodstream.com/e/ju53ufkqy9hc", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamtape.com/e/WgxgBAwOyGfbZxY", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://waaw.to/f/xtG7M9GL2MmV", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filelions.to/v/tnehp8g7zght", }, { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://plustream.com/embedb2/gHHRwqKdHgF5MYniuxGWnG8j_vIW77uC4a4vrO5iwLktMwS8ryr2i.bhH3SiGHw6PKNBoqxCHLDyc1Pw6EPhwg--", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://streamwish.to/e/mtb09rms1njz", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://filemoon.sx/e/r77fu9ez9nsv", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://voe.sx/e/cepsnqy0qgyo", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://doodstream.com/e/ny5qlz61x9xt", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://waaw.to/f/hlHhgFkmWHom", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://plustream.com/embedb2/DpAUEfJrAHkBAdb.6erU25OAypGXqMugDfQuWU2sTDBKHNQmIxzyq1b2Ma06kb7EgL6d4hdzw8quWDimAGnIvQ--", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad", + }, "url": "https://vidhidepro.com/v/lhaueh479t2v", }, ] @@ -168,33 +288,57 @@ exports[`Cuevana handle el camino 1`] = ` exports[`Cuevana handle la frontera s1e1 1`] = ` [ { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://streamwish.to/e/tpzbrzz7p41k", }, { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://filemoon.sx/e/nhbmj6o6zl0l", }, { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://vidhidepro.com/v/x5w6b0wys1fs", }, { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://voe.sx/e/nfal9zqu9yvy", }, { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://doodstream.com/e/dyvg7ofnnqgi", }, { - "countryCode": "es", - "title": "La frontera 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "La frontera 1x1", + }, "url": "https://waaw.to/f/goypdL0qGqks", }, ] @@ -203,33 +347,57 @@ exports[`Cuevana handle la frontera s1e1 1`] = ` exports[`Cuevana handle walking dead s1e1 1`] = ` [ { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://streamwish.to/e/gudk6hvc4qwl", }, { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://filemoon.sx/e/kp39yor1z0js", }, { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://voe.sx/e/4ahrkbnofnty", }, { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://doodstream.com/e/wvk23p39ikyd", }, { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://streamtape.com/e/qMjrKleBMpCLO0", }, { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://filelions.to/v/rvyogjvsejlh", }, ] diff --git a/src/source/__snapshots__/Einschalten.test.ts.snap b/src/source/__snapshots__/Einschalten.test.ts.snap index 46532c7..5d4a513 100644 --- a/src/source/__snapshots__/Einschalten.test.ts.snap +++ b/src/source/__snapshots__/Einschalten.test.ts.snap @@ -3,8 +3,12 @@ exports[`Einschalten handle superman 1`] = ` [ { - "countryCode": "de", - "title": "Superman.2025.German.DL.720p.WEB.H264-ZeroTwo", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Superman.2025.German.DL.720p.WEB.H264-ZeroTwo", + }, "url": "https://vide0.net/e/qtvqjblhu74i", }, ] diff --git a/src/source/__snapshots__/Eurostreaming.test.ts.snap b/src/source/__snapshots__/Eurostreaming.test.ts.snap index 6039179..7711f9a 100644 --- a/src/source/__snapshots__/Eurostreaming.test.ts.snap +++ b/src/source/__snapshots__/Eurostreaming.test.ts.snap @@ -3,13 +3,21 @@ exports[`Eurostreaming game of thrones s1e1 1`] = ` [ { - "countryCode": "it", - "title": "Il Trono di Spade 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Il Trono di Spade 1x1", + }, "url": "https://supervideo.cc/e/05jfivnr5hiw", }, { - "countryCode": "it", - "title": "Il Trono di Spade 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Il Trono di Spade 1x1", + }, "url": "https://dropload.io/embed-dub8oh3k3yk5.html", }, ] @@ -18,13 +26,21 @@ exports[`Eurostreaming game of thrones s1e1 1`] = ` exports[`Eurostreaming handle imdb black mirror s2e4 1`] = ` [ { - "countryCode": "it", - "title": "Black Mirror 2x4", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Black Mirror 2x4", + }, "url": "https://supervideo.cc/e/anf0clp2vb6y", }, { - "countryCode": "it", - "title": "Black Mirror 2x4", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Black Mirror 2x4", + }, "url": "https://dropload.io/embed-978xyyxi3ldq.html", }, ] @@ -33,13 +49,21 @@ exports[`Eurostreaming handle imdb black mirror s2e4 1`] = ` exports[`Eurostreaming handle lost s1e1 1`] = ` [ { - "countryCode": "it", - "title": "Lost 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Lost 1x1", + }, "url": "https://supervideo.cc/e/lgslt2lst4jv", }, { - "countryCode": "it", - "title": "Lost 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "Lost 1x1", + }, "url": "https://dropload.io/embed-vabynwo8glxz.html", }, ] @@ -48,13 +72,21 @@ exports[`Eurostreaming handle lost s1e1 1`] = ` exports[`Eurostreaming last of us s1e1 1`] = ` [ { - "countryCode": "it", - "title": "The Last of Us 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "The Last of Us 1x1", + }, "url": "https://supervideo.cc/e/ic1mmvrh5xv6", }, { - "countryCode": "it", - "title": "The Last of Us 1x1", + "meta": { + "countryCodes": [ + "it", + ], + "title": "The Last of Us 1x1", + }, "url": "https://dropload.io/embed-uyzfrh62n31l.html", }, ] diff --git a/src/source/__snapshots__/Frembed.test.ts.snap b/src/source/__snapshots__/Frembed.test.ts.snap index b569504..6937eaf 100644 --- a/src/source/__snapshots__/Frembed.test.ts.snap +++ b/src/source/__snapshots__/Frembed.test.ts.snap @@ -3,28 +3,48 @@ exports[`Frembed handle battle royal 1`] = ` [ { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://d-s.io/e/gtenfs0rffzq", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://netu.fanstream.live/e/p0QUHavRbQD0", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://jilliandescribecompany.com/e/lbeqm6ofmauq", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://video.streamtales.cc/player/frvod.php?url=https://streamtales.cc:8443/videos/3176.mp4", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://uqload.cx/embed-pnxmokgrigu9.html", }, ] @@ -33,23 +53,39 @@ exports[`Frembed handle battle royal 1`] = ` exports[`Frembed handle imdb black mirror s4e2 1`] = ` [ { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://d-s.io/e/dfx8me4un4ul", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://netu.fanstream.live/e/0DFgfkcXOsDP", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://jilliandescribecompany.com/e/cqy9oue7sv0g", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://ds2play.com/e/fzfvfq3ngig0", }, ] @@ -58,23 +94,39 @@ exports[`Frembed handle imdb black mirror s4e2 1`] = ` exports[`Frembed handle tmdb black mirror s4e2 1`] = ` [ { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://d-s.io/e/dfx8me4un4ul", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://netu.fanstream.live/e/0DFgfkcXOsDP", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://jilliandescribecompany.com/e/cqy9oue7sv0g", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://ds2play.com/e/fzfvfq3ngig0", }, ] diff --git a/src/source/__snapshots__/FrenchCloud.test.ts.snap b/src/source/__snapshots__/FrenchCloud.test.ts.snap index e7ce397..679cc7a 100644 --- a/src/source/__snapshots__/FrenchCloud.test.ts.snap +++ b/src/source/__snapshots__/FrenchCloud.test.ts.snap @@ -3,28 +3,43 @@ exports[`FrenchCloud handle imdb the devil's bath 1`] = ` [ { - "countryCode": "fr", - "referer": "https://frenchcloud.cam/movie/tt29141112", + "meta": { + "countryCodes": [ + "fr", + ], + }, "url": "https://supervideo.cc/e/ttnqt211ebtg", }, { - "countryCode": "fr", - "referer": "https://frenchcloud.cam/movie/tt29141112", + "meta": { + "countryCodes": [ + "fr", + ], + }, "url": "https://dropload.io/embed-w4yit5wiia9y.html", }, { - "countryCode": "fr", - "referer": "https://frenchcloud.cam/movie/tt29141112", + "meta": { + "countryCodes": [ + "fr", + ], + }, "url": "https://mixdrop.ag/e/8l3634dmt68rnp", }, { - "countryCode": "fr", - "referer": "https://frenchcloud.cam/movie/tt29141112", + "meta": { + "countryCodes": [ + "fr", + ], + }, "url": "https://streamtape.com/e/gjA1OQ4klyHxgJ", }, { - "countryCode": "fr", - "referer": "https://frenchcloud.cam/movie/tt29141112", + "meta": { + "countryCodes": [ + "fr", + ], + }, "url": "https://dood.to/e/osbcylatzy6m", }, ] diff --git a/src/source/__snapshots__/HomeCine.test.ts.snap b/src/source/__snapshots__/HomeCine.test.ts.snap index 7ce9e6d..01e8e4e 100644 --- a/src/source/__snapshots__/HomeCine.test.ts.snap +++ b/src/source/__snapshots__/HomeCine.test.ts.snap @@ -3,8 +3,12 @@ exports[`HomeCine does not return es content for mx 1`] = ` [ { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad (2019)", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad (2019)", + }, "url": "https://fastream.to/embed-kc5dvcy34x8p.html", }, ] @@ -13,8 +17,12 @@ exports[`HomeCine does not return es content for mx 1`] = ` exports[`HomeCine does not return mx content for es 1`] = ` [ { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad (2019)", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad (2019)", + }, "url": "https://fastream.to/embed-c9tmfzsbfj0z.html", }, ] @@ -23,13 +31,21 @@ exports[`HomeCine does not return mx content for es 1`] = ` exports[`HomeCine handle el camino 1`] = ` [ { - "countryCode": "mx", - "title": "El Camino: Una película de Breaking Bad (2019)", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "El Camino: Una película de Breaking Bad (2019)", + }, "url": "https://fastream.to/embed-kc5dvcy34x8p.html", }, { - "countryCode": "es", - "title": "El Camino: Una película de Breaking Bad (2019)", + "meta": { + "countryCodes": [ + "es", + ], + "title": "El Camino: Una película de Breaking Bad (2019)", + }, "url": "https://fastream.to/embed-c9tmfzsbfj0z.html", }, ] @@ -38,8 +54,12 @@ exports[`HomeCine handle el camino 1`] = ` exports[`HomeCine handle marvel - the punisher 1`] = ` [ { - "countryCode": "es", - "title": "Marvel - The Punisher 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "Marvel - The Punisher 1x1", + }, "url": "https://fastream.to/embed-ujq9uojoftog.html", }, ] @@ -48,13 +68,21 @@ exports[`HomeCine handle marvel - the punisher 1`] = ` exports[`HomeCine handle walking dead s1e1 1`] = ` [ { - "countryCode": "mx", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "mx", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://fastream.to/embed-nkztf1memuca.html", }, { - "countryCode": "es", - "title": "The Walking Dead 1x1", + "meta": { + "countryCodes": [ + "es", + ], + "title": "The Walking Dead 1x1", + }, "url": "https://fastream.to/embed-5bv96pdifz0b.html", }, ] diff --git a/src/source/__snapshots__/KinoGer.test.ts.snap b/src/source/__snapshots__/KinoGer.test.ts.snap index dfb569d..87b15c9 100644 --- a/src/source/__snapshots__/KinoGer.test.ts.snap +++ b/src/source/__snapshots__/KinoGer.test.ts.snap @@ -3,18 +3,30 @@ exports[`KinoGer handle imdb blood and sinners 1`] = ` [ { - "countryCode": "de", - "title": "Blood & Sinners (2025)", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Blood & Sinners (2025)", + }, "url": "https://fsst.online/embed/900576/", }, { - "countryCode": "de", - "title": "Blood & Sinners (2025)", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Blood & Sinners (2025)", + }, "url": "https://kinoger.pw/e/6zeREaMlrqREZPa", }, { - "countryCode": "de", - "title": "Blood & Sinners (2025)", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Blood & Sinners (2025)", + }, "url": "https://kinoger.re/#ge5fhb", }, ] @@ -23,13 +35,21 @@ exports[`KinoGer handle imdb blood and sinners 1`] = ` exports[`KinoGer handle imdb dead city s2e5 1`] = ` [ { - "countryCode": "de", - "title": "The Walking Dead: Dead City 2x5", + "meta": { + "countryCodes": [ + "de", + ], + "title": "The Walking Dead: Dead City 2x5", + }, "url": "https://supervideo.cc/e/bj6szat1pval", }, { - "countryCode": "de", - "title": "The Walking Dead: Dead City 2x5", + "meta": { + "countryCodes": [ + "de", + ], + "title": "The Walking Dead: Dead City 2x5", + }, "url": "https://kinoger.re/#x6tsx9", }, ] @@ -38,13 +58,21 @@ exports[`KinoGer handle imdb dead city s2e5 1`] = ` exports[`KinoGer handle imdb dead city s2e6 1`] = ` [ { - "countryCode": "de", - "title": "The Walking Dead: Dead City 2x6", + "meta": { + "countryCodes": [ + "de", + ], + "title": "The Walking Dead: Dead City 2x6", + }, "url": "https://fsst.online/embed/901994/", }, { - "countryCode": "de", - "title": "The Walking Dead: Dead City 2x6", + "meta": { + "countryCodes": [ + "de", + ], + "title": "The Walking Dead: Dead City 2x6", + }, "url": "https://kinoger.re/#ep1tcf", }, ] @@ -55,8 +83,12 @@ exports[`KinoGer handle missing episode imdb black mirror s3e4 1`] = `[]`; exports[`KinoGer handle no fsst via brokeback mountain 1`] = ` [ { - "countryCode": "de", - "title": "Brokeback Mountain (2005)", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Brokeback Mountain (2005)", + }, "url": "https://dooodster.com/e/1cfcevn6dg8shrfvht22odxw2lty18hr", }, ] diff --git a/src/source/__snapshots__/MegaKino.test.ts.snap b/src/source/__snapshots__/MegaKino.test.ts.snap index 146c5df..c873b4f 100644 --- a/src/source/__snapshots__/MegaKino.test.ts.snap +++ b/src/source/__snapshots__/MegaKino.test.ts.snap @@ -3,15 +3,21 @@ exports[`MegaKino handle imdb baymax 1`] = ` [ { - "countryCode": "de", - "referer": "https://megakino.si/films/693-baymax-riesiges-robowabohu.html", - "title": "Baymax - Riesiges Robowabohu", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Baymax - Riesiges Robowabohu", + }, "url": "https://voe.sx/e/8doq7gwiwtig", }, { - "countryCode": "de", - "referer": "https://megakino.si/films/693-baymax-riesiges-robowabohu.html", - "title": "Baymax - Riesiges Robowabohu", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Baymax - Riesiges Robowabohu", + }, "url": "https://watch.gxplayer.xyz/watch?v=MEKI92PU", }, ] diff --git a/src/source/__snapshots__/MeineCloud.test.ts.snap b/src/source/__snapshots__/MeineCloud.test.ts.snap index a262eb0..5a5f60d 100644 --- a/src/source/__snapshots__/MeineCloud.test.ts.snap +++ b/src/source/__snapshots__/MeineCloud.test.ts.snap @@ -3,23 +3,35 @@ exports[`MeineCloud handle imdb the devil's bath 1`] = ` [ { - "countryCode": "de", - "referer": "https://meinecloud.click/movie/tt29141112", + "meta": { + "countryCodes": [ + "de", + ], + }, "url": "https://supervideo.cc/e/q7i0sw1oytw3", }, { - "countryCode": "de", - "referer": "https://meinecloud.click/movie/tt29141112", + "meta": { + "countryCodes": [ + "de", + ], + }, "url": "https://dropload.io/embed-lyo2h1snpe5c.html", }, { - "countryCode": "de", - "referer": "https://meinecloud.click/movie/tt29141112", + "meta": { + "countryCodes": [ + "de", + ], + }, "url": "https://mixdrop.ag/e/3nzwveprim63or6", }, { - "countryCode": "de", - "referer": "https://meinecloud.click/movie/tt29141112", + "meta": { + "countryCodes": [ + "de", + ], + }, "url": "https://dood.to/e/sk1m9eumzyjj", }, ] diff --git a/src/source/__snapshots__/MostraGuarda.test.ts.snap b/src/source/__snapshots__/MostraGuarda.test.ts.snap index 7b5f81e..d343c00 100644 --- a/src/source/__snapshots__/MostraGuarda.test.ts.snap +++ b/src/source/__snapshots__/MostraGuarda.test.ts.snap @@ -3,23 +3,35 @@ exports[`MostraGuarda handle imdb the devil's bath 1`] = ` [ { - "countryCode": "it", - "referer": "https://mostraguarda.stream/movie/tt29141112", + "meta": { + "countryCodes": [ + "it", + ], + }, "url": "https://supervideo.cc/e/6vri8jyhla3n", }, { - "countryCode": "it", - "referer": "https://mostraguarda.stream/movie/tt29141112", + "meta": { + "countryCodes": [ + "it", + ], + }, "url": "https://dropload.io/embed-xsr90y2ltdyx.html", }, { - "countryCode": "it", - "referer": "https://mostraguarda.stream/movie/tt29141112", + "meta": { + "countryCodes": [ + "it", + ], + }, "url": "https://mixdrop.ag/e/vk196d6xfzwwo1", }, { - "countryCode": "it", - "referer": "https://mostraguarda.stream/movie/tt29141112", + "meta": { + "countryCodes": [ + "it", + ], + }, "url": "https://dood.to/e/37o04m33q3g5", }, ] diff --git a/src/source/__snapshots__/Movix.test.ts.snap b/src/source/__snapshots__/Movix.test.ts.snap index 03ff371..2ff67ba 100644 --- a/src/source/__snapshots__/Movix.test.ts.snap +++ b/src/source/__snapshots__/Movix.test.ts.snap @@ -3,63 +3,111 @@ exports[`Movix handle battle royal 1`] = ` [ { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://lecteur6.com/video/18085327b86002fc604c323b9a07f997", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://vidoza.net/embed-55hvynbdyqvc.html", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://wishonly.site/e/kykhuv6tscs8", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://darkibox.com/embed-5ghz7myucfjq.html", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://supervideo.cc/e/uieh17bid0ce", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://dood.li/e/gha75crrm8nf", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://filemoon.sx/e/c5lhlypfasmm", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://voe.sx/e/aghpmosgkhsu", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "http://vidmoly.me/embed-79w00r2qv88n.html", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://waaw.to/f/JWzt5eJSbpu8", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://veev.to/e/lu4c24e3aetf", }, { - "countryCode": "fr", - "title": "Battle Royale", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Battle Royale", + }, "url": "https://listeamed.net/e/eL2157Kzw8m5rw4", }, ] @@ -68,68 +116,120 @@ exports[`Movix handle battle royal 1`] = ` exports[`Movix handle tmdb black mirror s4e2 1`] = ` [ { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://uqload.net/embed-xjmhb7rbzdjh.html", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://lulustream.com/e/c3defa2cbtmm", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://wishonly.site/e/bzfdmjlrsqxk", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://vidoza.net/embed-i2e8gn4m64e1.html", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://filemoon.sx/e/0iuzfpt5cbx6", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://voe.sx/e/mustkdlxo67t", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "http://vidmoly.me/embed-2rls953tvhxc.html", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://waaw.to/f/GdQ6mHpxPdd8", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://veev.to/e/nifwerilwo0i", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://listeamed.net/e/8ozgENePwdLxmjA", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://coflix.upn.one/#x63hig", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://do7go.com/e/2zn4y7ueq1ta", }, { - "countryCode": "fr", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "fr", + ], + "title": "Black Mirror 4x2", + }, "url": "https://movearnpre.com/embed/eiel2p4z74kp", }, ] diff --git a/src/source/__snapshots__/PrimeWire.test.ts.snap b/src/source/__snapshots__/PrimeWire.test.ts.snap index 250371a..1215b54 100644 --- a/src/source/__snapshots__/PrimeWire.test.ts.snap +++ b/src/source/__snapshots__/PrimeWire.test.ts.snap @@ -3,59 +3,115 @@ exports[`PrimeWire handle el camino 1`] = ` [ { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/bv4b7tl9u1ij", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/wayeo7l1cw7d", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/xwhvakkexuob", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/paqi7qqku3zi", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/ysimllytd3zo", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/6QLoLq0O8DtObX", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/qMVMPa49M1hz8BR/El.Camino.A.Breaking.Bad.Movie.2019.1080p.WEBRip.x264_YTS.LT.mp4", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/9lw62yyosxc0", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/05fm25orfeux", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/j2pa25q11pdu", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamwish.to/09q73jf6t6jw", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/2bnmi", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/4njk6691b1k60d", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/ql0qrxnjb89nx8", }, ] @@ -64,59 +120,115 @@ exports[`PrimeWire handle el camino 1`] = ` exports[`PrimeWire handle el camino 2`] = ` [ { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/bv4b7tl9u1ij", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/wayeo7l1cw7d", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/xwhvakkexuob", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/paqi7qqku3zi", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/ysimllytd3zo", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/6QLoLq0O8DtObX", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/qMVMPa49M1hz8BR/El.Camino.A.Breaking.Bad.Movie.2019.1080p.WEBRip.x264_YTS.LT.mp4", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/9lw62yyosxc0", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/05fm25orfeux", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/j2pa25q11pdu", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamwish.to/09q73jf6t6jw", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/2bnmi", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/4njk6691b1k60d", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/ql0qrxnjb89nx8", }, ] @@ -125,139 +237,275 @@ exports[`PrimeWire handle el camino 2`] = ` exports[`PrimeWire handle imdb dead city s2e5 1`] = ` [ { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://bigwarp.cc/u66q6mybilfa", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://bigwarp.cc/aazhpuex9ppj", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://bigwarp.cc/4tb37obzt6re", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/odssr9dyjx5n", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/0i0j0qz5o478", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/eqmzrkv5kf5s", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filelions.to/f/fpru8wthtsf4", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/y8lrmf2t7jhp", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/te40pmzra8du", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://voe.sx/grsvpdbxvw3e", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/D2e3jR83rKTkPJw/", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamtape.com/v/Ap0GJoXglaFbqK/The.Walking.Dead.Dead.City.S02E05.The.Bird.Always.Knows.720p.AMZN.WEB-DL.DDP5.1.H.264-NTb.mkv.mp4", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://savefiles.com/i0bj9gazequy", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/1qfuy5yiq4r6", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/sn87i3ru7plx", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/8odyvc9bre16", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://dood.watch/d/reslxv9siz41", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamwish.to/jult1lcvqjlt", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamwish.to/s986lkdy8mb5", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamwish.to/h9p9qqgpq2lw", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/nlpj3q3ran17rv", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/q1pz4k94uxxkgvo", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://mixdrop.ag/f/9wvz9gp3b3q1llk", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filemoon.sx/d/ftowmfa7f0ge/The.Walking.Dead.Dead.City.S02E05.The.Bird.Always.Knows.720p.AMZN.WEB-DL.DDP5.1.H.264-NTb.mkv", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filemoon.sx/d/8wllnog0cyl7", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://streamplay.to/yrkzvpayg27o", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "http://vidmoly.me/w/okcwblnaqw3t", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://vidmoly.me/w/g1ob8a8h5pc8", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "http://vidmoly.me/w/46bi0sec0966", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://vidmoly.me/w/awjd3z0t0i9l", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://luluvdoo.com/d/au00sj11vqku", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filegram.to/erapbfgcdguq", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filegram.to/6q8gctbf79gc", }, { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://filegram.to/6yuyz0dgqu05", }, ] diff --git a/src/source/__snapshots__/Soaper.test.ts.snap b/src/source/__snapshots__/Soaper.test.ts.snap index 7466d9e..97e3526 100644 --- a/src/source/__snapshots__/Soaper.test.ts.snap +++ b/src/source/__snapshots__/Soaper.test.ts.snap @@ -3,8 +3,12 @@ exports[`Soaper handle imdb black mirror s4e2 1`] = ` [ { - "countryCode": "en", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "en", + ], + "title": "Black Mirror 4x2", + }, "url": "https://soaper.live/episode_5KDq78eGp1.html", }, ] @@ -13,8 +17,12 @@ exports[`Soaper handle imdb black mirror s4e2 1`] = ` exports[`Soaper handle imdb full metal jacket 1`] = ` [ { - "countryCode": "en", - "title": "Full Metal Jacket (1987)", + "meta": { + "countryCodes": [ + "en", + ], + "title": "Full Metal Jacket (1987)", + }, "url": "https://soaper.live/movie_d8kdeypDY9.html", }, ] @@ -23,8 +31,12 @@ exports[`Soaper handle imdb full metal jacket 1`] = ` exports[`Soaper handle lego heroes flash 1`] = ` [ { - "countryCode": "en", - "title": "LEGO DC Comics Super Heroes: The Flash (2018)", + "meta": { + "countryCodes": [ + "en", + ], + "title": "LEGO DC Comics Super Heroes: The Flash (2018)", + }, "url": "https://soaper.live/movie_lOGwQVEDV0.html", }, ] @@ -33,8 +45,12 @@ exports[`Soaper handle lego heroes flash 1`] = ` exports[`Soaper handle tmdb black mirror s4e2 1`] = ` [ { - "countryCode": "en", - "title": "Black Mirror 4x2", + "meta": { + "countryCodes": [ + "en", + ], + "title": "Black Mirror 4x2", + }, "url": "https://soaper.live/episode_5KDq78eGp1.html", }, ] diff --git a/src/source/__snapshots__/StreamKiste.test.ts.snap b/src/source/__snapshots__/StreamKiste.test.ts.snap index cf6f8dd..fce22ac 100644 --- a/src/source/__snapshots__/StreamKiste.test.ts.snap +++ b/src/source/__snapshots__/StreamKiste.test.ts.snap @@ -3,15 +3,21 @@ exports[`StreamKiste handle imdb black mirror s2e4 1`] = ` [ { - "countryCode": "de", - "referer": "https://streamkiste.taxi/movie/29992-black-mirror-stream-kostenlos.html", - "title": "Black Mirror 2x4", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Black Mirror 2x4", + }, "url": "https://supervideo.cc/embed-22go50mon0ke.html", }, { - "countryCode": "de", - "referer": "https://streamkiste.taxi/movie/29992-black-mirror-stream-kostenlos.html", - "title": "Black Mirror 2x4", + "meta": { + "countryCodes": [ + "de", + ], + "title": "Black Mirror 2x4", + }, "url": "https://dropload.io/embed-jvjwrkpijr0f.html", }, ] diff --git a/src/source/__snapshots__/VerHdLink.test.ts.snap b/src/source/__snapshots__/VerHdLink.test.ts.snap index 1a4ff6a..900f275 100644 --- a/src/source/__snapshots__/VerHdLink.test.ts.snap +++ b/src/source/__snapshots__/VerHdLink.test.ts.snap @@ -3,48 +3,75 @@ exports[`VerHdLink handle titanic 1`] = ` [ { - "countryCode": "mx", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "mx", + ], + }, "url": "https://supervideo.cc/e/g6okbr0kd790", }, { - "countryCode": "mx", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "mx", + ], + }, "url": "https://dropload.io/embed-jjjx9j5joiz8.html", }, { - "countryCode": "mx", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "mx", + ], + }, "url": "https://mixdrop.ag/e/vn0wx308fq984q", }, { - "countryCode": "mx", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "mx", + ], + }, "url": "https://streamtape.com/e/Bjp2vjrdBxsK82", }, { - "countryCode": "mx", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "mx", + ], + }, "url": "https://dood.to/e/rw7rxdfrbg09", }, { - "countryCode": "es", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "es", + ], + }, "url": "https://supervideo.cc/e/qe1fviow8uwy", }, { - "countryCode": "es", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "es", + ], + }, "url": "https://dropload.io/embed-ick4ti66vt6s.html", }, { - "countryCode": "es", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "es", + ], + }, "url": "https://mixdrop.ag/e/xokzmv61hrwe8o", }, { - "countryCode": "es", - "referer": "https://verhdlink.cam/movie/tt0120338", + "meta": { + "countryCodes": [ + "es", + ], + }, "url": "https://dood.to/e/gy8l8mb2i311", }, ] diff --git a/src/source/__snapshots__/VidSrc.test.ts.snap b/src/source/__snapshots__/VidSrc.test.ts.snap index b4c1f14..5f2432b 100644 --- a/src/source/__snapshots__/VidSrc.test.ts.snap +++ b/src/source/__snapshots__/VidSrc.test.ts.snap @@ -3,7 +3,11 @@ exports[`VidSrc handle imdb black mirror s4e2 1`] = ` [ { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://vidsrc.xyz/embed/tv/tt2085059/4/2", }, ] @@ -12,7 +16,11 @@ exports[`VidSrc handle imdb black mirror s4e2 1`] = ` exports[`VidSrc handle imdb full metal jacket 1`] = ` [ { - "countryCode": "en", + "meta": { + "countryCodes": [ + "en", + ], + }, "url": "https://vidsrc.xyz/embed/movie/tt0093058", }, ] diff --git a/src/source/__snapshots__/VixSrc.test.ts.snap b/src/source/__snapshots__/VixSrc.test.ts.snap index 2f63036..bbcf8dd 100644 --- a/src/source/__snapshots__/VixSrc.test.ts.snap +++ b/src/source/__snapshots__/VixSrc.test.ts.snap @@ -3,7 +3,11 @@ exports[`VixSrc handle imdb black mirror s4e2 1`] = ` [ { - "countryCode": "multi", + "meta": { + "countryCodes": [ + "multi", + ], + }, "url": "https://vixsrc.to/tv/42009/4/2", }, ] @@ -12,7 +16,11 @@ exports[`VixSrc handle imdb black mirror s4e2 1`] = ` exports[`VixSrc handle imdb full metal jacket 1`] = ` [ { - "countryCode": "multi", + "meta": { + "countryCodes": [ + "multi", + ], + }, "url": "https://vixsrc.to/movie/600", }, ] diff --git a/src/source/__snapshots__/XPrime.test.ts.snap b/src/source/__snapshots__/XPrime.test.ts.snap index 428e3a0..9d9fde7 100644 --- a/src/source/__snapshots__/XPrime.test.ts.snap +++ b/src/source/__snapshots__/XPrime.test.ts.snap @@ -3,8 +3,12 @@ exports[`XPrime handle alien: earth 1x1 1`] = ` [ { - "countryCode": "en", - "title": "Alien: Earth 1x1", + "meta": { + "countryCodes": [ + "en", + ], + "title": "Alien: Earth 1x1", + }, "url": "https://backend.xprime.tv/primebox?name=Alien%3A%20Earth&year=2025&season=1&episode=1", }, ] @@ -13,8 +17,12 @@ exports[`XPrime handle alien: earth 1x1 1`] = ` exports[`XPrime handle superman 1`] = ` [ { - "countryCode": "en", - "title": "Superman (2025)", + "meta": { + "countryCodes": [ + "en", + ], + "title": "Superman (2025)", + }, "url": "https://backend.xprime.tv/primebox?name=Superman&year=2025", }, ] diff --git a/src/types.ts b/src/types.ts index 6e2402d..18f0599 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,7 +41,7 @@ export enum BlockedReason { export interface Meta { bytes?: number | undefined; - countryCodes: CountryCode[]; + countryCodes?: CountryCode[]; height?: number | undefined; title?: string | undefined; } @@ -61,7 +61,7 @@ export interface UrlResult { label: string; sourceId: string; ttl?: number; - meta: Meta; + meta?: Meta; notWebReady?: boolean; requestHeaders?: Record; } diff --git a/src/utils/StreamResolver.test.ts b/src/utils/StreamResolver.test.ts index 3ac83d0..44dec94 100644 --- a/src/utils/StreamResolver.test.ts +++ b/src/utils/StreamResolver.test.ts @@ -91,7 +91,7 @@ describe('resolve', () => { public readonly baseUrl = 'https://example.com'; public readonly handleInternal = async (): Promise => { - return [{ countryCode: CountryCode.de, url: new URL('https://example.com') }]; + return [{ url: new URL('https://example.com'), meta: { countryCodes: [CountryCode.de] } }]; }; } diff --git a/src/utils/StreamResolver.ts b/src/utils/StreamResolver.ts index 7e0104d..2e91f4c 100644 --- a/src/utils/StreamResolver.ts +++ b/src/utils/StreamResolver.ts @@ -51,7 +51,7 @@ export class StreamResolver { this.logger.info(`${source.id} returned ${sourceResults.length} urls`, ctx); const sourceUrlResults = await Promise.all( - sourceResults.map(({ countryCode, title, url }) => this.extractorRegistry.handle(ctx, url, countryCode, title)), + sourceResults.map(({ url, meta }) => this.extractorRegistry.handle(ctx, url, meta)), ); urlResults.push(...sourceUrlResults.flat()); @@ -70,12 +70,12 @@ export class StreamResolver { await Promise.all(sourcePromises); urlResults.sort((a, b) => { - const heightComparison = (b.meta.height ?? 0) - (a.meta.height ?? 0); + const heightComparison = (b.meta?.height ?? 0) - (a.meta?.height ?? 0); if (heightComparison !== 0) { return heightComparison; } - const bytesComparison = (b.meta.bytes ?? 0) - (a.meta.bytes ?? 0); + const bytesComparison = (b.meta?.bytes ?? 0) - (a.meta?.bytes ?? 0); if (bytesComparison !== 0) { return bytesComparison; } @@ -102,7 +102,7 @@ export class StreamResolver { notWebReady: true, proxyHeaders: { request: urlResult.requestHeaders }, }), - ...(urlResult.meta.bytes && { videoSize: urlResult.meta.bytes }), + ...(urlResult.meta?.bytes && { videoSize: urlResult.meta.bytes }), }, })), ); @@ -142,11 +142,11 @@ export class StreamResolver { private buildName(ctx: Context, urlResult: UrlResult): string { let name = envGetAppName(); - urlResult.meta.countryCodes.forEach((countryCode) => { + urlResult.meta?.countryCodes?.forEach((countryCode) => { name += ` ${flagFromCountryCode(countryCode)}`; }); - if (urlResult.meta.height) { + if (urlResult.meta?.height) { name += ` ${urlResult.meta.height}p`; } @@ -160,12 +160,12 @@ export class StreamResolver { private buildTitle(ctx: Context, urlResult: UrlResult): string { const titleLines = []; - if (urlResult.meta.title) { + if (urlResult.meta?.title) { titleLines.push(urlResult.meta.title); } const titleDetailsLine = []; - if (urlResult.meta.bytes) { + if (urlResult.meta?.bytes) { titleDetailsLine.push(`💾 ${bytes.format(urlResult.meta.bytes, { unitSeparator: ' ' })}`); } titleDetailsLine.push(`🔗 ${urlResult.label}`);