
titanic-1997-3[subtitulado]
+mickey-17-2025-[latino]
1280x556, 1.4 GB + HD quality
1332x720, 1.1 GB @@ -270,7 +270,7 @@ $.cookie('aff', '149', { expires: 10 });
diff --git a/src/extractor/DoodStream.ts b/src/extractor/DoodStream.ts index 95fbc24..4ed8ff9 100644 --- a/src/extractor/DoodStream.ts +++ b/src/extractor/DoodStream.ts @@ -24,11 +24,7 @@ export class DoodStream implements Extractor { const html = await this.fetcher.text(ctx, new URL(normalizedUrl)); - const passMd5Match = html.match(/\/pass_md5\/[\w-]+\/([\w-]+)/); - if (!passMd5Match) { - // This will happen if either DoodStream does not like our IP or the video is not existing - return undefined; - } + const passMd5Match = html.match(/\/pass_md5\/[\w-]+\/([\w-]+)/) as string[]; const token = passMd5Match[1] as string; diff --git a/src/extractor/ExtractorRegistry.ts b/src/extractor/ExtractorRegistry.ts index cf2935f..3480ea7 100644 --- a/src/extractor/ExtractorRegistry.ts +++ b/src/extractor/ExtractorRegistry.ts @@ -6,6 +6,7 @@ import { Fetcher } from '../utils'; import { DoodStream } from './DoodStream'; import { Dropload } from './Dropload'; import { SuperVideo } from './SuperVideo'; +import { Mixdrop } from './Mixdrop'; export class ExtractorRegistry { private readonly logger: winston.Logger; @@ -17,6 +18,7 @@ export class ExtractorRegistry { this.embedExtractors = [ new DoodStream(fetcher), new Dropload(fetcher), + new Mixdrop(fetcher), new SuperVideo(fetcher), ]; this.urlResultCache = new TTLCache({ max: 1024 }); diff --git a/src/extractor/Mixdrop.ts b/src/extractor/Mixdrop.ts new file mode 100644 index 0000000..63e5c19 --- /dev/null +++ b/src/extractor/Mixdrop.ts @@ -0,0 +1,43 @@ +import { Extractor } from './types'; +import { extractUrlFromPacked, Fetcher } from '../utils'; +import { Context } from '../types'; +import bytes from 'bytes'; + +export class Mixdrop implements Extractor { + readonly id = 'mixdrop'; + + readonly label = 'Mixdrop'; + + readonly ttl = 900000; // 15m + + private readonly fetcher: Fetcher; + + constructor(fetcher: Fetcher) { + this.fetcher = fetcher; + } + + readonly supports = (url: URL): boolean => null !== url.host.match(/mixdrop/); + + readonly extract = async (ctx: Context, url: URL, countryCode: string) => { + const normalizedFullUrl = url.toString().replace('/e/', '/f/'); + const fullHtml = await this.fetcher.text(ctx, new URL(normalizedFullUrl)); + + const sizeMatch = fullHtml.match(/([\d.]+ ?[GM]B)/) as string[]; + + const normalizedEmbedUrl = url.toString().replace('/f/', '/e/'); + const embedHtml = await this.fetcher.text(ctx, new URL(normalizedEmbedUrl)); + + return { + url: extractUrlFromPacked(embedHtml, [/MDCore.wurl="(.*?)"/]), + label: this.label, + sourceId: `${this.id}_${countryCode.toLowerCase()}`, + height: 0, + bytes: bytes.parse(sizeMatch[1] as string) as number, + countryCode, + requestHeaders: { + 'Referer': url.origin, + 'User-Agent': this.fetcher.getUserAgentForIp(ctx.ip), + }, + }; + }; +} diff --git a/src/handler/FrenchCloud.test.ts b/src/handler/FrenchCloud.test.ts index b1e8b8a..cee903b 100644 --- a/src/handler/FrenchCloud.test.ts +++ b/src/handler/FrenchCloud.test.ts @@ -27,7 +27,7 @@ describe('FrenchCloud', () => { test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - expect(streams).toHaveLength(3); + expect(streams).toHaveLength(4); expect(streams[0]).toStrictEqual({ url: expect.any(URL), label: 'SuperVideo', @@ -47,6 +47,19 @@ describe('FrenchCloud', () => { }); expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); expect(streams[2]).toStrictEqual({ + url: expect.any(URL), + label: 'Mixdrop', + sourceId: 'mixdrop_fr', + height: 0, + bytes: 966703185, + countryCode: 'fr', + requestHeaders: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }); + expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?s=.*?e=.*?t=/); + expect(streams[3]).toStrictEqual({ url: expect.any(URL), label: 'DoodStream', sourceId: 'doodstream_fr', @@ -57,6 +70,6 @@ describe('FrenchCloud', () => { Referer: 'http://dood.to/', }, }); - expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); }); }); diff --git a/src/handler/MeineCloud.test.ts b/src/handler/MeineCloud.test.ts index ff65fef..b2a2fc1 100644 --- a/src/handler/MeineCloud.test.ts +++ b/src/handler/MeineCloud.test.ts @@ -27,7 +27,7 @@ describe('MeineCloud', () => { test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - expect(streams).toHaveLength(3); + expect(streams).toHaveLength(4); expect(streams[0]).toStrictEqual({ url: expect.any(URL), label: 'SuperVideo', @@ -47,6 +47,19 @@ describe('MeineCloud', () => { }); expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); expect(streams[2]).toStrictEqual({ + url: expect.any(URL), + label: 'Mixdrop', + sourceId: 'mixdrop_de', + height: 0, + bytes: 1256277934, + countryCode: 'de', + requestHeaders: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }); + expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?s=.*?e=.*?t=/); + expect(streams[3]).toStrictEqual({ url: expect.any(URL), label: 'DoodStream', sourceId: 'doodstream_de', @@ -57,6 +70,6 @@ describe('MeineCloud', () => { Referer: 'http://dood.to/', }, }); - expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); }); }); diff --git a/src/handler/MostraGuarda.test.ts b/src/handler/MostraGuarda.test.ts index 0b07098..8ea193c 100644 --- a/src/handler/MostraGuarda.test.ts +++ b/src/handler/MostraGuarda.test.ts @@ -27,7 +27,7 @@ describe('MostraGuarda', () => { test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - expect(streams).toHaveLength(3); + expect(streams).toHaveLength(4); expect(streams[0]).toStrictEqual({ url: expect.any(URL), label: 'SuperVideo', @@ -47,6 +47,19 @@ describe('MostraGuarda', () => { }); expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); expect(streams[2]).toStrictEqual({ + url: expect.any(URL), + label: 'Mixdrop', + sourceId: 'mixdrop_it', + height: 0, + bytes: 1170378588, + countryCode: 'it', + requestHeaders: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }); + expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?s=.*?e=.*?t=/); + expect(streams[3]).toStrictEqual({ url: expect.any(URL), label: 'DoodStream', sourceId: 'doodstream_it', @@ -57,6 +70,6 @@ describe('MostraGuarda', () => { Referer: 'http://dood.to/', }, }); - expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); }); }); diff --git a/src/handler/VerHdLink.test.ts b/src/handler/VerHdLink.test.ts index 693defa..a2d2e47 100644 --- a/src/handler/VerHdLink.test.ts +++ b/src/handler/VerHdLink.test.ts @@ -24,16 +24,16 @@ describe('VerHdLink', () => { expect(streams).toHaveLength(0); }); - test('handle titanic', async () => { - const streams = (await handler.handle(ctx, 'movie', 'tt0120338')).filter(stream => stream !== undefined); + test('handle mickey 17', async () => { + const streams = (await handler.handle(ctx, 'movie', 'tt12299608')).filter(stream => stream !== undefined); - expect(streams).toHaveLength(4); + expect(streams).toHaveLength(8); expect(streams[0]).toStrictEqual({ url: expect.any(URL), label: 'SuperVideo', sourceId: 'supervideo_mx', - height: 556, - bytes: 1503238553, + height: 720, + bytes: 1288490188, countryCode: 'mx', }); expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); @@ -41,28 +41,78 @@ describe('VerHdLink', () => { url: expect.any(URL), label: 'Dropload', sourceId: 'dropload_mx', - height: 556, - bytes: 1503238553, + height: 720, + bytes: 1181116006, countryCode: 'mx', }); expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); expect(streams[2]).toStrictEqual({ + url: expect.any(URL), + label: 'Mixdrop', + sourceId: 'mixdrop_mx', + height: 0, + bytes: 1299227607, + countryCode: 'mx', + requestHeaders: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }); + expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?s=.*?e=.*?t=/); + expect(streams[3]).toStrictEqual({ + url: expect.any(URL), + label: 'DoodStream', + sourceId: 'doodstream_mx', + height: 0, + bytes: 0, + countryCode: 'mx', + requestHeaders: { + Referer: 'http://dood.to/', + }, + }); + expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams[4]).toStrictEqual({ url: expect.any(URL), label: 'SuperVideo', sourceId: 'supervideo_es', - height: 544, - bytes: 1610612736, + height: 720, + bytes: 1288490188, countryCode: 'es', }); - expect(streams[2]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[3]).toStrictEqual({ + expect(streams[4]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams[5]).toStrictEqual({ url: expect.any(URL), label: 'Dropload', sourceId: 'dropload_es', - height: 544, - bytes: 1610612736, + height: 720, + bytes: 1181116006, countryCode: 'es', }); - expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams[5]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams[6]).toStrictEqual({ + url: expect.any(URL), + label: 'Mixdrop', + sourceId: 'mixdrop_es', + height: 0, + bytes: 1342177280, + countryCode: 'es', + requestHeaders: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }); + expect(streams[6]?.url.href).toMatch(/^https:\/\/.*?s=.*?e=.*?t=/); + expect(streams[7]).toStrictEqual({ + url: expect.any(URL), + label: 'DoodStream', + sourceId: 'doodstream_es', + height: 0, + bytes: 0, + countryCode: 'es', + requestHeaders: { + Referer: 'http://dood.to/', + }, + }); + expect(streams[7]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); }); }); diff --git a/src/utils/Fetcher.ts b/src/utils/Fetcher.ts index 87a3f62..4d735f6 100644 --- a/src/utils/Fetcher.ts +++ b/src/utils/Fetcher.ts @@ -37,7 +37,7 @@ export class Fetcher { return { ...config, headers: { - 'User-Agent': this.createUserAgentForIp(ctx.ip), + 'User-Agent': this.getUserAgentForIp(ctx.ip), 'Forwarded': `for=${ctx.ip}`, 'Referer': `${url.origin}`, 'X-Forwarded-For': ctx.ip, @@ -48,7 +48,7 @@ export class Fetcher { }; }; - private readonly createUserAgentForIp = (ip: string): string => { + public readonly getUserAgentForIp = (ip: string): string => { let userAgent = this.ipUserAgentCache.get(ip); if (userAgent) { return userAgent; diff --git a/src/utils/StreamResolver.test.ts b/src/utils/StreamResolver.test.ts index 171be4a..392cdb0 100644 --- a/src/utils/StreamResolver.test.ts +++ b/src/utils/StreamResolver.test.ts @@ -91,6 +91,38 @@ describe('resolve', () => { videoSize: 1073741824, }, }, + { + url: expect.any(String), + name: 'WebStreamr', + title: 'Mixdrop | 💾 1.17 GB | 🇩🇪', + behaviorHints: { + bingeGroup: 'webstreamr-mixdrop_de', + notWebReady: true, + proxyHeaders: { + request: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }, + videoSize: 1256277934, + }, + }, + { + url: expect.any(String), + name: 'WebStreamr', + title: 'Mixdrop | 💾 1.09 GB | 🇮🇹', + behaviorHints: { + bingeGroup: 'webstreamr-mixdrop_it', + notWebReady: true, + proxyHeaders: { + request: { + 'Referer': 'https://mixdrop.ag', + 'User-Agent': 'Fake UserAgent', + }, + }, + videoSize: 1170378588, + }, + }, { url: expect.any(String), name: 'WebStreamr', diff --git a/src/utils/__fixtures__/Fetcher/http:dood.toe2ld51itfvzib b/src/utils/__fixtures__/Fetcher/http:dood.toe2ld51itfvzib new file mode 100644 index 0000000..9aea138 --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/http:dood.toe2ld51itfvzib @@ -0,0 +1 @@ +
video you are looking for is not found.
video you are looking for is not found.
video you are looking for is not found.
video you are looking for is not found.
