
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 });
From 4c53161e98ca6ea24bdc76ba22fdf16785044f2c Mon Sep 17 00:00:00 2001 From: WebStreamr <210764791+webstreamr@users.noreply.github.com> Date: Fri, 16 May 2025 22:06:57 +0200 Subject: [PATCH] feat(extractor): implement Mixdrop --- src/extractor/DoodStream.ts | 6 +- src/extractor/ExtractorRegistry.ts | 2 + src/extractor/Mixdrop.ts | 43 ++ src/handler/FrenchCloud.test.ts | 17 +- src/handler/MeineCloud.test.ts | 17 +- src/handler/MostraGuarda.test.ts | 17 +- src/handler/VerHdLink.test.ts | 78 ++- src/utils/Fetcher.ts | 4 +- src/utils/StreamResolver.test.ts | 32 ++ .../Fetcher/http:dood.toe2ld51itfvzib | 1 + .../Fetcher/http:dood.toe4xpbdr8vx5qc | 1 + .../Fetcher/http:dood.toegy8l8mb2i311 | 1 - .../Fetcher/http:dood.toerw7rxdfrbg09 | 1 - ...b17485799f5b02f543xfjqlaupznxh9878fn3lc9db | 1 + ...77fee0aa7891eb270aly7u2yzord2mx08ozt1vch47 | 1 + ...tml => https:dropload.io96mnzbkw3gg5.html} | 60 +-- .../https:dropload.ioick4ti66vt6s.html | 502 ------------------ ...tml => https:dropload.iorlqh0n87n00f.html} | 60 +-- .../Fetcher/https:mixdrop.age3nzwveprim63or6 | 48 ++ .../Fetcher/https:mixdrop.age7k3wwq14cdw6w6p | 47 ++ .../Fetcher/https:mixdrop.agel7v73zqrfdj19z | 48 ++ .../Fetcher/https:mixdrop.agevk196d6xfzwwo1 | 47 ++ .../Fetcher/https:mixdrop.agexw01q6w4uk3rn7 | 47 ++ .../Fetcher/https:mixdrop.agf3nzwveprim63or6 | 213 ++++++++ .../Fetcher/https:mixdrop.agf7k3wwq14cdw6w6p | 213 ++++++++ .../Fetcher/https:mixdrop.agfl7v73zqrfdj19z | 213 ++++++++ .../Fetcher/https:mixdrop.agfvk196d6xfzwwo1 | 213 ++++++++ .../Fetcher/https:mixdrop.agfxw01q6w4uk3rn7 | 213 ++++++++ ...x2w09p => https:supervideo.cca0iiampow9za} | 76 +-- .../Fetcher/https:supervideo.ccqe1fviow8uwy | 401 -------------- ...0kd790 => https:supervideo.ccxrbn7pwrqj06} | 76 +-- ...338 => https:verhdlink.cammoviett12299608} | 48 +- src/utils/__mocks__/Fetcher.ts | 4 + src/utils/embed.ts | 4 +- 34 files changed, 1663 insertions(+), 1092 deletions(-) create mode 100644 src/extractor/Mixdrop.ts create mode 100644 src/utils/__fixtures__/Fetcher/http:dood.toe2ld51itfvzib create mode 100644 src/utils/__fixtures__/Fetcher/http:dood.toe4xpbdr8vx5qc delete mode 100644 src/utils/__fixtures__/Fetcher/http:dood.toegy8l8mb2i311 delete mode 100644 src/utils/__fixtures__/Fetcher/http:dood.toerw7rxdfrbg09 create mode 100644 src/utils/__fixtures__/Fetcher/http:dood.topass_md5206423523-139-197-1747424982-e1005ab93b4c5ab17485799f5b02f543xfjqlaupznxh9878fn3lc9db create mode 100644 src/utils/__fixtures__/Fetcher/http:dood.topass_md5206735176-139-197-1747424982-9973f08f7b32c977fee0aa7891eb270aly7u2yzord2mx08ozt1vch47 rename src/utils/__fixtures__/Fetcher/{https:dropload.io3rj3nyfors6b.html => https:dropload.io96mnzbkw3gg5.html} (84%) delete mode 100644 src/utils/__fixtures__/Fetcher/https:dropload.ioick4ti66vt6s.html rename src/utils/__fixtures__/Fetcher/{https:dropload.iojjjx9j5joiz8.html => https:dropload.iorlqh0n87n00f.html} (74%) create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.age3nzwveprim63or6 create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.age7k3wwq14cdw6w6p create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agel7v73zqrfdj19z create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agevk196d6xfzwwo1 create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agexw01q6w4uk3rn7 create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agf3nzwveprim63or6 create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agf7k3wwq14cdw6w6p create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agfl7v73zqrfdj19z create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agfvk196d6xfzwwo1 create mode 100644 src/utils/__fixtures__/Fetcher/https:mixdrop.agfxw01q6w4uk3rn7 rename src/utils/__fixtures__/Fetcher/{https:supervideo.cc8xc9f2x2w09p => https:supervideo.cca0iiampow9za} (81%) delete mode 100644 src/utils/__fixtures__/Fetcher/https:supervideo.ccqe1fviow8uwy rename src/utils/__fixtures__/Fetcher/{https:supervideo.ccg6okbr0kd790 => https:supervideo.ccxrbn7pwrqj06} (80%) rename src/utils/__fixtures__/Fetcher/{https:verhdlink.cammoviett0120338 => https:verhdlink.cammoviett12299608} (87%) 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.
