From e90c89af89347561a8c61ea004ac725674f30aa5 Mon Sep 17 00:00:00 2001 From: WebStreamr <210764791+webstreamr@users.noreply.github.com> Date: Wed, 21 May 2025 20:37:35 +0000 Subject: [PATCH] test: use snapshots, also use fixtures for axios errors --- jest.config.ts | 3 +- jest.setup.ts | 7 ++ src/handler/CineHDPlus.test.ts | 44 +-------- src/handler/Eurostreaming.test.ts | 23 +---- src/handler/Frembed.test.ts | 37 +------- src/handler/FrenchCloud.test.ts | 45 +--------- src/handler/KinoKiste.test.ts | 23 +---- src/handler/MeineCloud.test.ts | 45 +--------- src/handler/MostraGuarda.test.ts | 45 +--------- src/handler/VerHdLink.test.ts | 61 +------------ .../__snapshots__/CineHDPlus.test.ts.snap | 43 +++++++++ .../__snapshots__/Eurostreaming.test.ts.snap | 22 +++++ .../__snapshots__/Frembed.test.ts.snap | 35 ++++++++ .../__snapshots__/FrenchCloud.test.ts.snap | 42 +++++++++ .../__snapshots__/KinoKiste.test.ts.snap | 22 +++++ .../__snapshots__/MeineCloud.test.ts.snap | 42 +++++++++ .../__snapshots__/MostraGuarda.test.ts.snap | 42 +++++++++ .../__snapshots__/VerHdLink.test.ts.snap | 56 ++++++++++++ src/utils/StreamResolver.test.ts | 90 +------------------ ...8.htmlhttps:ds2play.comefzfvfq3ngig0.error | 1 + .../https:ds2play.comefzfvfq3ngig0.error | 1 + ...iesid61945andsa1andepi1andidTypetmdb.error | 1 + .../Fetcher/https:some-url.test.error | 1 + .../https:streamtape.comeBjp2vjrdBxsK82.error | 1 + .../https:streamtape.comegjA1OQ4klyHxgJ.error | 1 + src/utils/__mocks__/Fetcher.ts | 50 ++++++++--- .../__snapshots__/StreamResolver.test.ts.snap | 86 ++++++++++++++++++ 27 files changed, 454 insertions(+), 415 deletions(-) create mode 100644 jest.setup.ts create mode 100644 src/handler/__snapshots__/CineHDPlus.test.ts.snap create mode 100644 src/handler/__snapshots__/Eurostreaming.test.ts.snap create mode 100644 src/handler/__snapshots__/Frembed.test.ts.snap create mode 100644 src/handler/__snapshots__/FrenchCloud.test.ts.snap create mode 100644 src/handler/__snapshots__/KinoKiste.test.ts.snap create mode 100644 src/handler/__snapshots__/MeineCloud.test.ts.snap create mode 100644 src/handler/__snapshots__/MostraGuarda.test.ts.snap create mode 100644 src/handler/__snapshots__/VerHdLink.test.ts.snap create mode 100644 src/utils/__fixtures__/Fetcher/https:ahvsh.come83izf7qzwpaehttps:netu.frembed.fune0DFgfkcXOsDPhttps:likessb.come7yjgl1x56n08.htmlhttps:ds2play.comefzfvfq3ngig0.error create mode 100644 src/utils/__fixtures__/Fetcher/https:ds2play.comefzfvfq3ngig0.error create mode 100644 src/utils/__fixtures__/Fetcher/https:frembed.clubapiseriesid61945andsa1andepi1andidTypetmdb.error create mode 100644 src/utils/__fixtures__/Fetcher/https:some-url.test.error create mode 100644 src/utils/__fixtures__/Fetcher/https:streamtape.comeBjp2vjrdBxsK82.error create mode 100644 src/utils/__fixtures__/Fetcher/https:streamtape.comegjA1OQ4klyHxgJ.error create mode 100644 src/utils/__snapshots__/StreamResolver.test.ts.snap diff --git a/jest.config.ts b/jest.config.ts index 47815d3..31ce4b3 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -21,8 +21,9 @@ const config: Config = { statements: 100, }, }, + resetMocks: true, resetModules: true, - restoreMocks: true, + setupFilesAfterEnv: ['/jest.setup.ts'], testEnvironment: 'node', transform: { '^.+.tsx?$': ['ts-jest', {}], diff --git a/jest.setup.ts b/jest.setup.ts new file mode 100644 index 0000000..2ac8cec --- /dev/null +++ b/jest.setup.ts @@ -0,0 +1,7 @@ +jest.mock('randomstring', () => ({ + generate: jest.fn(() => 'mocked-random-string'), +})); + +beforeEach(() => { + jest.spyOn(Date, 'now').mockImplementation(() => 639837296000); +}); diff --git a/src/handler/CineHDPlus.test.ts b/src/handler/CineHDPlus.test.ts index c43bcdb..ccd1596 100644 --- a/src/handler/CineHDPlus.test.ts +++ b/src/handler/CineHDPlus.test.ts @@ -14,62 +14,22 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { es: 'on', mx: 'on' } describe('CineHDPlus', () => { test('does not handle non imdb series', async () => { const streams = await handler.handle(ctx, 'series', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent series gracefully', async () => { const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1'); - expect(streams).toHaveLength(0); }); test('handle imdb black mirror s2e3 (mx)', async () => { const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:3')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(2); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_mx', - height: 360, - bytes: 146800640, - countryCode: 'mx', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_mx', - height: 360, - bytes: 146800640, - countryCode: 'mx', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams).toMatchSnapshot(); }); test('handle imdb babylon 5 s2e3 (es)', async () => { const streams = (await handler.handle(ctx, 'series', 'tt0105946:2:3')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(2); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_es', - height: 344, - bytes: 219571814, - countryCode: 'es', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_es', - height: 344, - bytes: 219571814, - countryCode: 'es', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams).toMatchSnapshot(); }); test('does not return mx results for es and vice-versa', async () => { diff --git a/src/handler/Eurostreaming.test.ts b/src/handler/Eurostreaming.test.ts index 149197a..da8a983 100644 --- a/src/handler/Eurostreaming.test.ts +++ b/src/handler/Eurostreaming.test.ts @@ -14,37 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } }; describe('Eurostreaming', () => { test('does not handle non imdb series', async () => { const streams = await handler.handle(ctx, 'series', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent series gracefully', async () => { const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1'); - expect(streams).toHaveLength(0); }); test('handle imdb black mirror s2e4', async () => { const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:4')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(2); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_it', - height: 1080, - bytes: 875875532, - countryCode: 'it', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_it', - height: 1080, - bytes: 875875532, - countryCode: 'it', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/Frembed.test.ts b/src/handler/Frembed.test.ts index d0fb993..aa5a78c 100644 --- a/src/handler/Frembed.test.ts +++ b/src/handler/Frembed.test.ts @@ -14,51 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } }; describe('Frembed', () => { test('does not handle non imdb series', async () => { const streams = await handler.handle(ctx, 'series', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent series gracefully', async () => { const streams = await handler.handle(ctx, 'series', 'tt4352342:1:1'); - expect(streams).toHaveLength(0); }); test('handle imdb black mirror s4e2', async () => { const streams = (await handler.handle(ctx, 'series', 'tt2085059:4:2')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(3); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'DoodStream', - sourceId: 'doodstream_fr', - height: 0, - bytes: 0, - countryCode: 'fr', - requestHeaders: { - Referer: 'http://dood.to/', - }, - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'netu.frembed.art', - sourceId: 'external_fr', - height: 0, - bytes: 0, - countryCode: 'fr', - }); - expect(streams[1]?.url.href).toMatch(/netu\.frembed\.art/); - expect(streams[2]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'johnalwayssame.com', - sourceId: 'external_fr', - height: 0, - bytes: 0, - countryCode: 'fr', - }); - expect(streams[2]?.url.href).toMatch(/johnalwayssame\.com/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/FrenchCloud.test.ts b/src/handler/FrenchCloud.test.ts index f7333aa..ba77395 100644 --- a/src/handler/FrenchCloud.test.ts +++ b/src/handler/FrenchCloud.test.ts @@ -14,59 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { fr: 'on' } }; describe('FrenchCloud', () => { test('does not handle non imdb movies', async () => { const streams = await handler.handle(ctx, 'movie', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent movies gracefully', async () => { const streams = await handler.handle(ctx, 'movie', 'tt12345678'); - expect(streams).toHaveLength(0); }); test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(4); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_fr', - height: 720, - bytes: 966682214, - countryCode: 'fr', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_fr', - height: 720, - bytes: 966682214, - countryCode: 'fr', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[2]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'mixdrop.ag', - sourceId: 'external_fr', - height: 0, - bytes: 0, - countryCode: 'fr', - }); - expect(streams[2]?.url.href).toMatch(/mixdrop\.ag/); - expect(streams[3]).toStrictEqual({ - url: expect.any(URL), - label: 'DoodStream', - sourceId: 'doodstream_fr', - height: 0, - bytes: 0, - countryCode: 'fr', - requestHeaders: { - Referer: 'http://dood.to/', - }, - }); - expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/KinoKiste.test.ts b/src/handler/KinoKiste.test.ts index 138d0f2..3ebecd0 100644 --- a/src/handler/KinoKiste.test.ts +++ b/src/handler/KinoKiste.test.ts @@ -14,37 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } }; describe('KinoKiste', () => { test('does not handle non imdb series', async () => { const streams = await handler.handle(ctx, 'series', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent series gracefully', async () => { const streams = await handler.handle(ctx, 'series', 'tt12345678:1:1'); - expect(streams).toHaveLength(0); }); test('handle imdb black mirror s2e4', async () => { const streams = (await handler.handle(ctx, 'series', 'tt2085059:2:4')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(2); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_de', - height: 720, - bytes: 733793484, - countryCode: 'de', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_de', - height: 720, - bytes: 733793484, - countryCode: 'de', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/MeineCloud.test.ts b/src/handler/MeineCloud.test.ts index 4e47ad9..8b86d30 100644 --- a/src/handler/MeineCloud.test.ts +++ b/src/handler/MeineCloud.test.ts @@ -14,59 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { de: 'on' } }; describe('MeineCloud', () => { test('does not handle non imdb movies', async () => { const streams = await handler.handle(ctx, 'movie', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent movies gracefully', async () => { const streams = await handler.handle(ctx, 'movie', 'tt12345678'); - expect(streams).toHaveLength(0); }); test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(4); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_de', - height: 720, - bytes: 1073741824, - countryCode: 'de', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_de', - height: 1080, - bytes: 1395864371, - countryCode: 'de', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[2]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'mixdrop.ag', - sourceId: 'external_de', - height: 0, - bytes: 0, - countryCode: 'de', - }); - expect(streams[2]?.url.href).toMatch(/mixdrop\.ag/); - expect(streams[3]).toStrictEqual({ - url: expect.any(URL), - label: 'DoodStream', - sourceId: 'doodstream_de', - height: 0, - bytes: 0, - countryCode: 'de', - requestHeaders: { - Referer: 'http://dood.to/', - }, - }); - expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/MostraGuarda.test.ts b/src/handler/MostraGuarda.test.ts index bf132e0..18cacae 100644 --- a/src/handler/MostraGuarda.test.ts +++ b/src/handler/MostraGuarda.test.ts @@ -14,59 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { it: 'on' } }; describe('MostraGuarda', () => { test('does not handle non imdb movies', async () => { const streams = await handler.handle(ctx, 'movie', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent movies gracefully', async () => { const streams = await handler.handle(ctx, 'movie', 'tt12345678'); - expect(streams).toHaveLength(0); }); test('handle imdb the devil\'s bath', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt29141112')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(4); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_it', - height: 720, - bytes: 1181116006, - countryCode: 'it', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_it', - height: 720, - bytes: 1181116006, - countryCode: 'it', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[2]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'mixdrop.ag', - sourceId: 'external_it', - height: 0, - bytes: 0, - countryCode: 'it', - }); - expect(streams[2]?.url.href).toMatch(/mixdrop\.ag/); - expect(streams[3]).toStrictEqual({ - url: expect.any(URL), - label: 'DoodStream', - sourceId: 'doodstream_it', - height: 0, - bytes: 0, - countryCode: 'it', - requestHeaders: { - Referer: 'http://dood.to/', - }, - }); - expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?token.*?expiry/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/VerHdLink.test.ts b/src/handler/VerHdLink.test.ts index cc651f5..dc1a451 100644 --- a/src/handler/VerHdLink.test.ts +++ b/src/handler/VerHdLink.test.ts @@ -14,75 +14,16 @@ const ctx: Context = { id: 'id', ip: '127.0.0.1', config: { es: 'on', mx: 'on' } describe('VerHdLink', () => { test('does not handle non imdb movies', async () => { const streams = await handler.handle(ctx, 'movie', 'kitsu:123'); - expect(streams).toHaveLength(0); }); test('handles non-existent movies gracefully', async () => { const streams = await handler.handle(ctx, 'movie', 'tt12345678'); - expect(streams).toHaveLength(0); }); test('handle titanic', async () => { const streams = (await handler.handle(ctx, 'movie', 'tt0120338')).filter(stream => stream !== undefined); - - expect(streams).toHaveLength(6); - expect(streams[0]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_mx', - height: 556, - bytes: 1503238553, - countryCode: 'mx', - }); - expect(streams[0]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[1]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_mx', - height: 556, - bytes: 1503238553, - countryCode: 'mx', - }); - expect(streams[1]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[2]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'mixdrop.ag', - sourceId: 'external_mx', - height: 0, - bytes: 0, - countryCode: 'mx', - }); - expect(streams[2]?.url.href).toMatch(/mixdrop\.ag/); - expect(streams[3]).toStrictEqual({ - url: expect.any(URL), - label: 'SuperVideo', - sourceId: 'supervideo_es', - height: 544, - bytes: 1610612736, - countryCode: 'es', - }); - expect(streams[3]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[4]).toStrictEqual({ - url: expect.any(URL), - label: 'Dropload', - sourceId: 'dropload_es', - height: 544, - bytes: 1610612736, - countryCode: 'es', - }); - expect(streams[4]?.url.href).toMatch(/^https:\/\/.*?.m3u8/); - expect(streams[5]).toStrictEqual({ - url: expect.any(URL), - isExternal: true, - label: 'mixdrop.ag', - sourceId: 'external_es', - height: 0, - bytes: 0, - countryCode: 'es', - }); - expect(streams[5]?.url.href).toMatch(/mixdrop\.ag/); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/handler/__snapshots__/CineHDPlus.test.ts.snap b/src/handler/__snapshots__/CineHDPlus.test.ts.snap new file mode 100644 index 0000000..bc0df7d --- /dev/null +++ b/src/handler/__snapshots__/CineHDPlus.test.ts.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CineHDPlus handle imdb babylon 5 s2e3 (es) 1`] = ` +[ + { + "bytes": 219571814, + "countryCode": "es", + "height": 344, + "label": "SuperVideo", + "sourceId": "supervideo_es", + "url": "https://hfs294.serversicuro.cc/hls/,dnzpealv5xg4a3gyvavh52jryms77x3wwakzvgy2ly3yfusnblczjnvvweua,.urlset/master.m3u8", + }, + { + "bytes": 219571814, + "countryCode": "es", + "height": 344, + "label": "Dropload", + "sourceId": "dropload_es", + "url": "https://srv29.dropload.io/hls2/01/00295/957hny8mzrvh_h/master.m3u8?t=VpcZJYlXpp0FJb0Nh0yJR0nyI8-SKB2tGFwpcTx9v2w&s=1747213958&e=14400&f=1476559&i=0.0&sp=0&ii=130.61.236.204", + }, +] +`; + +exports[`CineHDPlus handle imdb black mirror s2e3 (mx) 1`] = ` +[ + { + "bytes": 146800640, + "countryCode": "mx", + "height": 360, + "label": "SuperVideo", + "sourceId": "supervideo_mx", + "url": "https://hfs299.serversicuro.cc/hls/,dnzpff262hg4a3gyvcex5ojxt23vrieljzut7xhjydwidlbp4g7nqswkpfwq,.urlset/master.m3u8", + }, + { + "bytes": 146800640, + "countryCode": "mx", + "height": 360, + "label": "Dropload", + "sourceId": "dropload_mx", + "url": "https://srv27.dropload.io/hls2/01/00075/t058ulwwkwn6_h/master.m3u8?t=GkktV2WgIMrvrgNLyjn6Bxeo2fArEAz6y9nSi_6dVAs&s=1747213564&e=14400&f=376684&i=0.0&sp=0&ii=130.61.236.204", + }, +] +`; diff --git a/src/handler/__snapshots__/Eurostreaming.test.ts.snap b/src/handler/__snapshots__/Eurostreaming.test.ts.snap new file mode 100644 index 0000000..89b77a5 --- /dev/null +++ b/src/handler/__snapshots__/Eurostreaming.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Eurostreaming handle imdb black mirror s2e4 1`] = ` +[ + { + "bytes": 875875532, + "countryCode": "it", + "height": 1080, + "label": "SuperVideo", + "sourceId": "supervideo_it", + "url": "https://hfs309.serversicuro.cc/hls/dnzpdeoe5xg4a3gyvaqx5ojpswtxjd5a22rklgah7,khdhascykjrixs7huqq,obtfascykj3vna7rk5a,.urlset/master.m3u8", + }, + { + "bytes": 875875532, + "countryCode": "it", + "height": 1080, + "label": "Dropload", + "sourceId": "dropload_it", + "url": "https://srv24.dropload.io/hls2/01/00289/978xyyxi3ldq_h/master.m3u8?t=unq9qxjYC-zgcmoXiSQ6rkMu7qHrSpN_pWOc95IPYwk&s=1747217151&e=14400&f=1448116&i=0.0&sp=0&ii=130.61.236.204", + }, +] +`; diff --git a/src/handler/__snapshots__/Frembed.test.ts.snap b/src/handler/__snapshots__/Frembed.test.ts.snap new file mode 100644 index 0000000..8842d02 --- /dev/null +++ b/src/handler/__snapshots__/Frembed.test.ts.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Frembed handle imdb black mirror s4e2 1`] = ` +[ + { + "bytes": 0, + "countryCode": "fr", + "height": 0, + "label": "DoodStream", + "requestHeaders": { + "Referer": "http://dood.to/", + }, + "sourceId": "doodstream_fr", + "url": "https://ee317r.cloudatacdn.com/u5kj63yvxddlsdgge7qremqqka27irwiupc4k7o5cikbh7fdq4j4mwzraf7q/1y4sn4ndoi~undefined?token=fh3rdlhjvao7chgxndsi3ra7&expiry=639837296000", + }, + { + "bytes": 0, + "countryCode": "fr", + "height": 0, + "isExternal": true, + "label": "netu.frembed.art", + "sourceId": "external_fr", + "url": "https://netu.frembed.art/e/0DFgfkcXOsDP", + }, + { + "bytes": 0, + "countryCode": "fr", + "height": 0, + "isExternal": true, + "label": "johnalwayssame.com", + "sourceId": "external_fr", + "url": "https://johnalwayssame.com/e/cqy9oue7sv0g", + }, +] +`; diff --git a/src/handler/__snapshots__/FrenchCloud.test.ts.snap b/src/handler/__snapshots__/FrenchCloud.test.ts.snap new file mode 100644 index 0000000..3ebc5da --- /dev/null +++ b/src/handler/__snapshots__/FrenchCloud.test.ts.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FrenchCloud handle imdb the devil's bath 1`] = ` +[ + { + "bytes": 966682214, + "countryCode": "fr", + "height": 720, + "label": "SuperVideo", + "sourceId": "supervideo_fr", + "url": "https://hfs292.serversicuro.cc/hls/,dnzpfwdj5dg4a3gyvbwx5lbvtu65tuwysrizwl7puv6u7dkpym3afmw5hnga,.urlset/master.m3u8", + }, + { + "bytes": 966682214, + "countryCode": "fr", + "height": 720, + "label": "Dropload", + "sourceId": "dropload_fr", + "url": "https://srv28.dropload.io/hls2/01/00215/w4yit5wiia9y_h/master.m3u8?t=ujTsd4AYOdgaA6EhuMJDrC-xYzMVEbqrrd3qsoZ8iAY&s=1747054860&e=14400&f=1076096&i=0.0&sp=0&ii=130.61.236.204", + }, + { + "bytes": 0, + "countryCode": "fr", + "height": 0, + "isExternal": true, + "label": "mixdrop.ag", + "sourceId": "external_fr", + "url": "https://mixdrop.ag/e/l7v73zqrfdj19z", + }, + { + "bytes": 0, + "countryCode": "fr", + "height": 0, + "label": "DoodStream", + "requestHeaders": { + "Referer": "http://dood.to/", + }, + "sourceId": "doodstream_fr", + "url": "https://ty1053vs.cloudatacdn.com/u5kjz2cvh7blsdgge4mmgoifjigorrf6o6mlfwqbndqkhkcvu43dcrk7wfga/6zwayqucuf~undefined?token=y0n5z6g48kft9apq8imziegv&expiry=639837296000", + }, +] +`; diff --git a/src/handler/__snapshots__/KinoKiste.test.ts.snap b/src/handler/__snapshots__/KinoKiste.test.ts.snap new file mode 100644 index 0000000..6ec310b --- /dev/null +++ b/src/handler/__snapshots__/KinoKiste.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`KinoKiste handle imdb black mirror s2e4 1`] = ` +[ + { + "bytes": 733793484, + "countryCode": "de", + "height": 720, + "label": "SuperVideo", + "sourceId": "supervideo_de", + "url": "https://hfs279.serversicuro.cc/hls/,dnzpervt3xg4a3gyvdix52ttsqpj5jgxs7keazdmhvru2uyin5g7f67a5u4a,.urlset/master.m3u8", + }, + { + "bytes": 733793484, + "countryCode": "de", + "height": 720, + "label": "Dropload", + "sourceId": "dropload_de", + "url": "https://srv34.dropload.io/hls2/01/00007/jvjwrkpijr0f_h/master.m3u8?t=9B5k5sYgDD2kS_549tQ4WYh1o74hO1QR9zzyXrVRqv4&s=1746903685&e=14400&f=35986&srv=srv27&i=0.0&sp=0&ii=130.61.236.204&p1=srv27&p2=srv27", + }, +] +`; diff --git a/src/handler/__snapshots__/MeineCloud.test.ts.snap b/src/handler/__snapshots__/MeineCloud.test.ts.snap new file mode 100644 index 0000000..5d6cb07 --- /dev/null +++ b/src/handler/__snapshots__/MeineCloud.test.ts.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MeineCloud handle imdb the devil's bath 1`] = ` +[ + { + "bytes": 1073741824, + "countryCode": "de", + "height": 720, + "label": "SuperVideo", + "sourceId": "supervideo_de", + "url": "https://hfs290.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p56thhyqu6ra3olbfa,.urlset/master.m3u8", + }, + { + "bytes": 1395864371, + "countryCode": "de", + "height": 1080, + "label": "Dropload", + "sourceId": "dropload_de", + "url": "https://srv27.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=qTZsRGIMnUjXsQ4o2LRLlsT4tGG1AdJrRBRDGttNLPM&s=1746903744&e=14400&f=987607&i=0.0&sp=0&ii=130.61.236.204", + }, + { + "bytes": 0, + "countryCode": "de", + "height": 0, + "isExternal": true, + "label": "mixdrop.ag", + "sourceId": "external_de", + "url": "https://mixdrop.ag/e/3nzwveprim63or6", + }, + { + "bytes": 0, + "countryCode": "de", + "height": 0, + "label": "DoodStream", + "requestHeaders": { + "Referer": "http://dood.to/", + }, + "sourceId": "doodstream_de", + "url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rtpmbzgb5tornooa/k9wk0js17e~undefined?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000", + }, +] +`; diff --git a/src/handler/__snapshots__/MostraGuarda.test.ts.snap b/src/handler/__snapshots__/MostraGuarda.test.ts.snap new file mode 100644 index 0000000..87cb8ab --- /dev/null +++ b/src/handler/__snapshots__/MostraGuarda.test.ts.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MostraGuarda handle imdb the devil's bath 1`] = ` +[ + { + "bytes": 1181116006, + "countryCode": "it", + "height": 720, + "label": "SuperVideo", + "sourceId": "supervideo_it", + "url": "https://hfs279.serversicuro.cc/hls/,dnzpejj427g4a3gyvbth53rxqewqgqqzp3wr6zwutysvud7zroar3cb75d3q,.urlset/master.m3u8", + }, + { + "bytes": 1181116006, + "countryCode": "it", + "height": 720, + "label": "Dropload", + "sourceId": "dropload_it", + "url": "https://srv34.dropload.io/hls2/01/00200/xsr90y2ltdyx_h/master.m3u8?t=avFOZ-10hDxo6__iaoSUrq7o4yY3ldLw6XDlXxpXF8E&s=1747059001&e=14400&f=1004116&srv=srv24&i=0.0&sp=0&ii=130.61.236.204&p1=srv24&p2=srv24", + }, + { + "bytes": 0, + "countryCode": "it", + "height": 0, + "isExternal": true, + "label": "mixdrop.ag", + "sourceId": "external_it", + "url": "https://mixdrop.ag/e/vk196d6xfzwwo1", + }, + { + "bytes": 0, + "countryCode": "it", + "height": 0, + "label": "DoodStream", + "requestHeaders": { + "Referer": "http://dood.to/", + }, + "sourceId": "doodstream_it", + "url": "https://kk892as.cloudatacdn.com/u5kj7j7s27d3sdgge5woezkbi4pu672wktq3aqujw47rbpl3xog2gtqnw2xa/awn9rgotuu~undefined?token=aw2v2d8uhbrj2ky54d573ujk&expiry=639837296000", + }, +] +`; diff --git a/src/handler/__snapshots__/VerHdLink.test.ts.snap b/src/handler/__snapshots__/VerHdLink.test.ts.snap new file mode 100644 index 0000000..0338cfd --- /dev/null +++ b/src/handler/__snapshots__/VerHdLink.test.ts.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VerHdLink handle titanic 1`] = ` +[ + { + "bytes": 1503238553, + "countryCode": "mx", + "height": 556, + "label": "SuperVideo", + "sourceId": "supervideo_mx", + "url": "https://hfs292.serversicuro.cc/hls/,dnzpejlw37g4a3gyvdzh5p3xttjqd3ccheao6qexhjbv4ecdpod7hxlh4a7a,.urlset/master.m3u8", + }, + { + "bytes": 1503238553, + "countryCode": "mx", + "height": 556, + "label": "Dropload", + "sourceId": "dropload_mx", + "url": "https://srv22.dropload.io/hls2/01/00046/jjjx9j5joiz8_h/master.m3u8?t=_sOFVDjKUqBAK_gJtH7YVQnCm6nj7kr4df3PNsKeKAA&s=1747060128&e=14400&f=230703&i=0.0&sp=0&ii=130.61.236.204", + }, + { + "bytes": 0, + "countryCode": "mx", + "height": 0, + "isExternal": true, + "label": "mixdrop.ag", + "sourceId": "external_mx", + "url": "https://mixdrop.ag/e/vn0wx308fq984q", + }, + { + "bytes": 1610612736, + "countryCode": "es", + "height": 544, + "label": "SuperVideo", + "sourceId": "supervideo_es", + "url": "https://hfs295.serversicuro.cc/hls/,dnzpfv3w37g4a3gyvdzh5kjeyi4vtoyw4x4it5gxl4zzwiv4t3fxkvagrhea,.urlset/master.m3u8", + }, + { + "bytes": 1610612736, + "countryCode": "es", + "height": 544, + "label": "Dropload", + "sourceId": "dropload_es", + "url": "https://srv23.dropload.io/hls2/01/00046/ick4ti66vt6s_h/master.m3u8?t=OUbNiE0qRNUy69JP4AIj1bGcV5kaljyNrAUlCYWGVOs&s=1747060128&e=14400&f=230700&i=0.0&sp=0&ii=130.61.236.204", + }, + { + "bytes": 0, + "countryCode": "es", + "height": 0, + "isExternal": true, + "label": "mixdrop.ag", + "sourceId": "external_es", + "url": "https://mixdrop.ag/e/xokzmv61hrwe8o", + }, +] +`; diff --git a/src/utils/StreamResolver.test.ts b/src/utils/StreamResolver.test.ts index 534f95b..2aecf07 100644 --- a/src/utils/StreamResolver.test.ts +++ b/src/utils/StreamResolver.test.ts @@ -28,7 +28,7 @@ describe('resolve', () => { }); test('returns handler errors as stream', async () => { - jest.spyOn(fetcher, 'text').mockRejectedValue('ups, an error occurred.'); + const fetcherSpy = jest.spyOn(fetcher, 'text').mockRejectedValue('ups, an error occurred.'); const streams = await streamResolver.resolve(ctx, [meineCloud], 'movie', 'tt123456789'); @@ -37,104 +37,22 @@ describe('resolve', () => { title: '❌ Error with handler "meinecloud". Please create an issue if this persists. Request-id: id', ytId: 'E4WlUXrJgy4', }]); + + fetcherSpy.mockRestore(); }); test('returns empty array if no handler found anything', async () => { const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', 'tt12345678'); - expect(streams).toStrictEqual([]); }); test('returns empty array if no handler supported the type', async () => { const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'series', 'tt12345678:1:1'); - expect(streams).toStrictEqual([]); }); test('returns sorted results', async () => { const streams = await streamResolver.resolve(ctx, [meineCloud, mostraGuarda], 'movie', 'tt29141112'); - - expect(streams).toStrictEqual([ - { - url: expect.any(String), - name: 'WebStreamr 1080p', - title: 'Dropload | 💾 1.3 GB | 🇩🇪', - behaviorHints: { - bingeGroup: 'webstreamr-dropload_de', - videoSize: 1395864371, - }, - }, - { - url: expect.any(String), - name: 'WebStreamr 720p', - title: 'Dropload | 💾 1.1 GB | 🇮🇹', - behaviorHints: { - bingeGroup: 'webstreamr-dropload_it', - videoSize: 1181116006, - }, - }, - { - url: expect.any(String), - name: 'WebStreamr 720p', - title: 'SuperVideo | 💾 1.1 GB | 🇮🇹', - behaviorHints: { - bingeGroup: 'webstreamr-supervideo_it', - videoSize: 1181116006, - }, - }, - { - url: expect.any(String), - name: 'WebStreamr 720p', - title: 'SuperVideo | 💾 1 GB | 🇩🇪', - behaviorHints: { - bingeGroup: 'webstreamr-supervideo_de', - videoSize: 1073741824, - }, - }, - { - url: expect.any(String), - name: 'WebStreamr', - title: 'DoodStream | 🇩🇪', - behaviorHints: { - bingeGroup: 'webstreamr-doodstream_de', - notWebReady: true, - proxyHeaders: { - request: { - Referer: 'http://dood.to/', - }, - }, - }, - }, - { - url: expect.any(String), - name: 'WebStreamr', - title: 'DoodStream | 🇮🇹', - behaviorHints: { - bingeGroup: 'webstreamr-doodstream_it', - notWebReady: true, - proxyHeaders: { - request: { - Referer: 'http://dood.to/', - }, - }, - }, - }, - { - externalUrl: 'https://mixdrop.ag/e/3nzwveprim63or6', - name: 'WebStreamr external', - title: 'mixdrop.ag | 🇩🇪', - behaviorHints: { - bingeGroup: 'webstreamr-external_de', - }, - }, - { - externalUrl: 'https://mixdrop.ag/e/vk196d6xfzwwo1', - name: 'WebStreamr external', - title: 'mixdrop.ag | 🇮🇹', - behaviorHints: { - bingeGroup: 'webstreamr-external_it', - }, - }, - ]); + expect(streams).toMatchSnapshot(); }); }); diff --git a/src/utils/__fixtures__/Fetcher/https:ahvsh.come83izf7qzwpaehttps:netu.frembed.fune0DFgfkcXOsDPhttps:likessb.come7yjgl1x56n08.htmlhttps:ds2play.comefzfvfq3ngig0.error b/src/utils/__fixtures__/Fetcher/https:ahvsh.come83izf7qzwpaehttps:netu.frembed.fune0DFgfkcXOsDPhttps:likessb.come7yjgl1x56n08.htmlhttps:ds2play.comefzfvfq3ngig0.error new file mode 100644 index 0000000..f13a70b --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:ahvsh.come83izf7qzwpaehttps:netu.frembed.fune0DFgfkcXOsDPhttps:likessb.come7yjgl1x56n08.htmlhttps:ds2play.comefzfvfq3ngig0.error @@ -0,0 +1 @@ +{"message":"socket hang up","code":"ECONNRESET"} \ No newline at end of file diff --git a/src/utils/__fixtures__/Fetcher/https:ds2play.comefzfvfq3ngig0.error b/src/utils/__fixtures__/Fetcher/https:ds2play.comefzfvfq3ngig0.error new file mode 100644 index 0000000..df87c8f --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:ds2play.comefzfvfq3ngig0.error @@ -0,0 +1 @@ +{"message":"unable to verify the first certificate","code":"UNABLE_TO_VERIFY_LEAF_SIGNATURE"} \ No newline at end of file diff --git a/src/utils/__fixtures__/Fetcher/https:frembed.clubapiseriesid61945andsa1andepi1andidTypetmdb.error b/src/utils/__fixtures__/Fetcher/https:frembed.clubapiseriesid61945andsa1andepi1andidTypetmdb.error new file mode 100644 index 0000000..d73315c --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:frembed.clubapiseriesid61945andsa1andepi1andidTypetmdb.error @@ -0,0 +1 @@ +{"message":"Request failed with status code 404","code":"ERR_BAD_REQUEST","response":{"status":404,"statusText":"Not Found","data":"{\"error\":\"Series not found\"}"}} \ No newline at end of file diff --git a/src/utils/__fixtures__/Fetcher/https:some-url.test.error b/src/utils/__fixtures__/Fetcher/https:some-url.test.error new file mode 100644 index 0000000..70557bb --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:some-url.test.error @@ -0,0 +1 @@ +{"message":"getaddrinfo ENOTFOUND some-url.test","code":"ENOTFOUND"} \ No newline at end of file diff --git a/src/utils/__fixtures__/Fetcher/https:streamtape.comeBjp2vjrdBxsK82.error b/src/utils/__fixtures__/Fetcher/https:streamtape.comeBjp2vjrdBxsK82.error new file mode 100644 index 0000000..17ad0a8 --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:streamtape.comeBjp2vjrdBxsK82.error @@ -0,0 +1 @@ +{"message":"Request failed with status code 404","code":"ERR_BAD_REQUEST","response":{"status":404,"statusText":"Not Found","data":"\n\n
\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t

Video not found!

\n\t\t\t\t

Maybe it got deleted by the creator!

\n\t\t\t\tGo to Streamtape.com\n\t\t\t
\n\t\t
\n\t\t\t
\n
\n\n\n
\n\t
\n\t\t
% buffered00:00
00:00
00:00
Go to Streamtape.com
\n\t
\n
\n\n\n\n
ad
\n\n\n\n\n\n\n\t\t\n\n\n\t\t\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"}} \ No newline at end of file diff --git a/src/utils/__fixtures__/Fetcher/https:streamtape.comegjA1OQ4klyHxgJ.error b/src/utils/__fixtures__/Fetcher/https:streamtape.comegjA1OQ4klyHxgJ.error new file mode 100644 index 0000000..050253f --- /dev/null +++ b/src/utils/__fixtures__/Fetcher/https:streamtape.comegjA1OQ4klyHxgJ.error @@ -0,0 +1 @@ +{"message":"Request failed with status code 404","code":"ERR_BAD_REQUEST","response":{"status":404,"statusText":"Not Found","data":"\n\n
\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t

Video not found!

\n\t\t\t\t

Maybe it got deleted by the creator!

\n\t\t\t\tGo to Streamtape.com\n\t\t\t
\n\t\t
\n\t\t\t
\n
\n\n\n
\n\t
\n\t\t
% buffered00:00
00:00
00:00
Go to Streamtape.com
\n\t
\n
\n\n\n\n
ad
\n\n\n\n\n\n\n\t\t\n\n\n\t\t\t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"}} \ No newline at end of file diff --git a/src/utils/__mocks__/Fetcher.ts b/src/utils/__mocks__/Fetcher.ts index b6dd500..75f402c 100644 --- a/src/utils/__mocks__/Fetcher.ts +++ b/src/utils/__mocks__/Fetcher.ts @@ -1,5 +1,5 @@ import fs from 'node:fs'; -import Axios, { AxiosRequestConfig } from 'axios'; +import Axios, { AxiosError, AxiosRequestConfig } from 'axios'; import slugify from 'slugify'; import { Context } from '../../types'; @@ -7,28 +7,50 @@ export class Fetcher { readonly text = async (_ctx: Context, url: URL, config?: AxiosRequestConfig): Promise => { const path = `${__dirname}/../__fixtures__/Fetcher/${slugify(url.href)}`; - if (fs.existsSync(path)) { - return fs.readFileSync(path).toString(); - } else { - const text = (await Axios.create().get(url.href, this.getConfig(config))).data; - - fs.writeFileSync(path, text); - - return text; - } + return this.fixtureWrapper(path, async () => (await Axios.create().get(url.href, this.getConfig(config))).data); }; readonly textPost = async (_ctx: Context, url: URL, data: unknown, config?: AxiosRequestConfig): Promise => { const path = `${__dirname}/../__fixtures__/Fetcher/post-${slugify(url.href)}-${slugify(JSON.stringify(data))}`; - if (fs.existsSync(path)) { + return this.fixtureWrapper(path, async () => (await Axios.create().post(url.href, data, this.getConfig(config))).data); + }; + + private readonly fixtureWrapper = async (path: string, callable: () => Promise) => { + const errorPath = `${path}.error`; + + if (fs.existsSync(errorPath)) { + const fixtureError = JSON.parse(fs.readFileSync(errorPath).toString()); + throw new AxiosError(fixtureError.message, fixtureError.code, undefined, undefined, fixtureError.response); + } else if (fs.existsSync(path)) { return fs.readFileSync(path).toString(); } else { - const text = (await Axios.create().post(url.href, data, this.getConfig(config))).data; + let response; - fs.writeFileSync(path, text); + try { + response = await callable(); + } catch (error) { + if (error instanceof AxiosError) { + const fixtureError = JSON.stringify({ + message: error.message, + code: error.code, + ...(error.response && { + response: { + status: error.response.status, + statusText: error.response.statusText, + data: error.response.data, + }, + }), + }); + fs.writeFileSync(errorPath, fixtureError); + } - return text; + throw error; + } + + fs.writeFileSync(path, response); + + return response; } }; diff --git a/src/utils/__snapshots__/StreamResolver.test.ts.snap b/src/utils/__snapshots__/StreamResolver.test.ts.snap new file mode 100644 index 0000000..f7a4b07 --- /dev/null +++ b/src/utils/__snapshots__/StreamResolver.test.ts.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`resolve returns sorted results 1`] = ` +[ + { + "behaviorHints": { + "bingeGroup": "webstreamr-dropload_de", + "videoSize": 1395864371, + }, + "name": "WebStreamr 1080p", + "title": "Dropload | 💾 1.3 GB | 🇩🇪", + "url": "https://srv27.dropload.io/hls2/01/00197/lyo2h1snpe5c_h/master.m3u8?t=qTZsRGIMnUjXsQ4o2LRLlsT4tGG1AdJrRBRDGttNLPM&s=1746903744&e=14400&f=987607&i=0.0&sp=0&ii=130.61.236.204", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-dropload_it", + "videoSize": 1181116006, + }, + "name": "WebStreamr 720p", + "title": "Dropload | 💾 1.1 GB | 🇮🇹", + "url": "https://srv34.dropload.io/hls2/01/00200/xsr90y2ltdyx_h/master.m3u8?t=avFOZ-10hDxo6__iaoSUrq7o4yY3ldLw6XDlXxpXF8E&s=1747059001&e=14400&f=1004116&srv=srv24&i=0.0&sp=0&ii=130.61.236.204&p1=srv24&p2=srv24", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-supervideo_it", + "videoSize": 1181116006, + }, + "name": "WebStreamr 720p", + "title": "SuperVideo | 💾 1.1 GB | 🇮🇹", + "url": "https://hfs279.serversicuro.cc/hls/,dnzpejj427g4a3gyvbth53rxqewqgqqzp3wr6zwutysvud7zroar3cb75d3q,.urlset/master.m3u8", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-supervideo_de", + "videoSize": 1073741824, + }, + "name": "WebStreamr 720p", + "title": "SuperVideo | 💾 1 GB | 🇩🇪", + "url": "https://hfs290.serversicuro.cc/hls/,dnzpfi3d27g4a3gyvbmh5klwtl65qh654hyjtgupd6p56thhyqu6ra3olbfa,.urlset/master.m3u8", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-doodstream_de", + "notWebReady": true, + "proxyHeaders": { + "request": { + "Referer": "http://dood.to/", + }, + }, + }, + "name": "WebStreamr", + "title": "DoodStream | 🇩🇪", + "url": "https://aa360cc.cloudatacdn.com/u5kjv4jxytd3sdgge5uogji5dg4huat2pxrm2qibdbm5rtpmbzgb5tornooa/k9wk0js17e~undefined?token=7uebebipnnhusa4xnyea1er4&expiry=639837296000", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-doodstream_it", + "notWebReady": true, + "proxyHeaders": { + "request": { + "Referer": "http://dood.to/", + }, + }, + }, + "name": "WebStreamr", + "title": "DoodStream | 🇮🇹", + "url": "https://kk892as.cloudatacdn.com/u5kj7j7s27d3sdgge5woezkbi4pu672wktq3aqujw47rbpl3xog2gtqnw2xa/awn9rgotuu~undefined?token=aw2v2d8uhbrj2ky54d573ujk&expiry=639837296000", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-external_de", + }, + "externalUrl": "https://mixdrop.ag/e/3nzwveprim63or6", + "name": "WebStreamr external", + "title": "mixdrop.ag | 🇩🇪", + }, + { + "behaviorHints": { + "bingeGroup": "webstreamr-external_it", + }, + "externalUrl": "https://mixdrop.ag/e/vk196d6xfzwwo1", + "name": "WebStreamr external", + "title": "mixdrop.ag | 🇮🇹", + }, +] +`;