mirror of
https://github.com/movie-web/providers.git
synced 2025-09-13 13:33:25 +00:00
Merge branch 'dev' of https://github.com/Ciarands/mw-providers into dev
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { buildProviders } from "@/entrypoint/builder";
|
||||
import { ScrapeMedia } from "@/entrypoint/utils/media";
|
||||
import { targets } from "@/entrypoint/utils/targets";
|
||||
import { makeStandardFetcher } from "@/fetchers/standardFetch";
|
||||
import { Embed, Sourcerer, SourcererEmbed } from "@/providers/base";
|
||||
import { TestTypes } from "./providerUtils";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ProviderControls } from "@/entrypoint/controls";
|
||||
import { makeSimpleProxyFetcher } from "@/fetchers/simpleProxy";
|
||||
import { buildProviders } from '@/entrypoint/builder';
|
||||
import { ScrapeMedia } from '@/entrypoint/utils/media';
|
||||
import { targets } from '@/entrypoint/utils/targets';
|
||||
import { makeStandardFetcher } from '@/fetchers/standardFetch';
|
||||
import { Embed, Sourcerer, SourcererEmbed } from '@/providers/base';
|
||||
import { TestTypes } from './providerUtils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ProviderControls } from '@/entrypoint/controls';
|
||||
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
|
||||
|
||||
export interface TestEmbedOptions {
|
||||
embed: Embed;
|
||||
@@ -18,18 +18,16 @@ export interface TestEmbedOptions {
|
||||
embeds: number;
|
||||
streams?: number;
|
||||
error?: boolean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function makeBaseEmbedProviders() {
|
||||
const builder = buildProviders()
|
||||
.setTarget(targets.ANY)
|
||||
.setFetcher(makeStandardFetcher(fetch));
|
||||
const builder = buildProviders().setTarget(targets.ANY).setFetcher(makeStandardFetcher(fetch));
|
||||
return builder;
|
||||
}
|
||||
|
||||
export function testEmbed(ops: TestEmbedOptions) {
|
||||
if (ops.testSuite.length === 0) throw new Error("Test suite must have at least one test");
|
||||
if (ops.testSuite.length === 0) throw new Error('Test suite must have at least one test');
|
||||
describe(`embed:${ops.source.id}:${ops.embed.id}`, () => {
|
||||
ops.testSuite.forEach((test) => {
|
||||
describe(`test ${test.title}`, async () => {
|
||||
@@ -37,8 +35,11 @@ export function testEmbed(ops: TestEmbedOptions) {
|
||||
const results = await providers.runSourceScraper({
|
||||
id: ops.source.id,
|
||||
media: test,
|
||||
})
|
||||
if (results.embeds.length !== ops.expect.embeds) throw new Error(`Embeds don't match expected amount of embeds (${ops.source.id}, ${ops.embed.id}, got ${results.embeds.length} but expected ${ops.expect.embeds})`);
|
||||
});
|
||||
if (results.embeds.length !== ops.expect.embeds)
|
||||
throw new Error(
|
||||
`Embeds don't match expected amount of embeds (${ops.source.id}, ${ops.embed.id}, got ${results.embeds.length} but expected ${ops.expect.embeds})`,
|
||||
);
|
||||
return results.embeds;
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ export function testEmbed(ops: TestEmbedOptions) {
|
||||
const result = await providers.runEmbedScraper({
|
||||
id: ops.embed.id,
|
||||
url: embedUrl,
|
||||
})
|
||||
});
|
||||
if (ops.debug) console.log(result);
|
||||
streamCount = (result.stream ?? []).length;
|
||||
} catch (err) {
|
||||
@@ -62,12 +63,11 @@ export function testEmbed(ops: TestEmbedOptions) {
|
||||
|
||||
for (const t of ops.types) {
|
||||
const builder = makeBaseEmbedProviders().addSource(ops.source).addEmbed(ops.embed);
|
||||
if (t === 'standard') {}
|
||||
else if (t === 'ip:standard')
|
||||
builder.enableConsistentIpForRequests();
|
||||
if (t === 'standard') {
|
||||
} else if (t === 'ip:standard') builder.enableConsistentIpForRequests();
|
||||
else if (t === 'proxied') {
|
||||
if (!process.env.MOVIE_WEB_PROXY_URL)
|
||||
throw new Error("Cant use proxied test without setting MOVIE_WEB_PROXY_URL env");
|
||||
throw new Error('Cant use proxied test without setting MOVIE_WEB_PROXY_URL env');
|
||||
builder.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch));
|
||||
}
|
||||
const providers = builder.build();
|
||||
@@ -76,15 +76,15 @@ export function testEmbed(ops: TestEmbedOptions) {
|
||||
embeds.forEach((embed, i) => {
|
||||
it(`${t} - embed ${i}`, async () => {
|
||||
await runTest(providers, embed.url);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
it(`${t} - embed ??`, () => {
|
||||
throw new Error("Failed to get streams: " + err);
|
||||
})
|
||||
throw new Error('Failed to get streams: ' + err);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import dotenv from 'dotenv';
|
||||
import { febboxMp4Scraper } from "@/providers/embeds/febbox/mp4";
|
||||
import { testEmbed } from "./embedUtils";
|
||||
import { showboxScraper } from "@/providers/sources/showbox";
|
||||
import { testMedia } from "./testMedia";
|
||||
import { flixhqScraper } from "@/providers/sources/flixhq";
|
||||
import { upcloudScraper } from "@/providers/embeds/upcloud";
|
||||
import { goMoviesScraper } from "@/providers/sources/gomovies";
|
||||
import { smashyStreamScraper } from "@/providers/sources/smashystream";
|
||||
import { smashyStreamDScraper } from "@/providers/embeds/smashystream/dued";
|
||||
import { febboxMp4Scraper } from '@/providers/embeds/febbox/mp4';
|
||||
import { testEmbed } from './embedUtils';
|
||||
import { showboxScraper } from '@/providers/sources/showbox';
|
||||
import { testMedia } from './testMedia';
|
||||
import { flixhqScraper } from '@/providers/sources/flixhq';
|
||||
import { upcloudScraper } from '@/providers/embeds/upcloud';
|
||||
import { goMoviesScraper } from '@/providers/sources/gomovies';
|
||||
import { smashyStreamScraper } from '@/providers/sources/smashystream';
|
||||
import { smashyStreamDScraper } from '@/providers/embeds/smashystream/dued';
|
||||
import { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
|
||||
import { vidsrcScraper } from '@/providers/sources/vidsrc';
|
||||
import { vidSrcToScraper } from '@/providers/sources/vidsrcto';
|
||||
@@ -26,8 +26,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: upcloudScraper,
|
||||
@@ -37,8 +37,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: upcloudScraper,
|
||||
@@ -48,8 +48,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: smashyStreamDScraper,
|
||||
@@ -59,8 +59,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: vidsrcembedScraper,
|
||||
@@ -70,8 +70,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: vidplayScraper,
|
||||
@@ -81,8 +81,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: fileMoonScraper,
|
||||
@@ -92,8 +92,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 1,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: upcloudScraper,
|
||||
@@ -103,8 +103,8 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 2,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testEmbed({
|
||||
embed: mixdropScraper,
|
||||
@@ -114,5 +114,5 @@ testEmbed({
|
||||
expect: {
|
||||
embeds: 2,
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { ScrapeMedia } from "@/entrypoint/utils/media";
|
||||
import { Embed, Sourcerer, SourcererEmbed } from "@/providers/base";
|
||||
import { buildProviders } from "@/entrypoint/builder";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { makeStandardFetcher } from "@/fetchers/standardFetch";
|
||||
import { ProviderControls } from "@/entrypoint/controls";
|
||||
import { NotFoundError } from "@/utils/errors";
|
||||
import { targets } from "@/entrypoint/utils/targets";
|
||||
import { getBuiltinEmbeds } from "@/entrypoint/providers";
|
||||
import { makeSimpleProxyFetcher } from "@/fetchers/simpleProxy";
|
||||
import { ScrapeMedia } from '@/entrypoint/utils/media';
|
||||
import { Embed, Sourcerer, SourcererEmbed } from '@/providers/base';
|
||||
import { buildProviders } from '@/entrypoint/builder';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { makeStandardFetcher } from '@/fetchers/standardFetch';
|
||||
import { ProviderControls } from '@/entrypoint/controls';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { targets } from '@/entrypoint/utils/targets';
|
||||
import { getBuiltinEmbeds } from '@/entrypoint/providers';
|
||||
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
|
||||
|
||||
export type TestTypes = 'standard' | 'ip:standard' | 'proxied';
|
||||
|
||||
@@ -21,20 +21,18 @@ export interface TestSourceOptions {
|
||||
streams?: number;
|
||||
error?: boolean;
|
||||
notfound?: boolean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function makeBaseProviders() {
|
||||
const builder = buildProviders()
|
||||
.setTarget(targets.ANY)
|
||||
.setFetcher(makeStandardFetcher(fetch));
|
||||
const builder = buildProviders().setTarget(targets.ANY).setFetcher(makeStandardFetcher(fetch));
|
||||
const embeds = getBuiltinEmbeds();
|
||||
embeds.forEach(embed => builder.addEmbed(embed));
|
||||
embeds.forEach((embed) => builder.addEmbed(embed));
|
||||
return builder;
|
||||
}
|
||||
|
||||
export function testSource(ops: TestSourceOptions) {
|
||||
if (ops.testSuite.length === 0) throw new Error("Test suite must have at least one test");
|
||||
if (ops.testSuite.length === 0) throw new Error('Test suite must have at least one test');
|
||||
describe(`source:${ops.source.id}`, () => {
|
||||
ops.testSuite.forEach((test) => {
|
||||
describe(`test ${test.title}`, () => {
|
||||
@@ -48,16 +46,14 @@ export function testSource(ops: TestSourceOptions) {
|
||||
const result = await providers.runSourceScraper({
|
||||
id: ops.source.id,
|
||||
media: test,
|
||||
})
|
||||
});
|
||||
if (ops.debug) console.log(result);
|
||||
streamCount = (result.stream ?? []).length;
|
||||
embedCount = result.embeds.length;
|
||||
} catch (err) {
|
||||
if (ops.debug) console.log(err);
|
||||
if (err instanceof NotFoundError)
|
||||
hasNotFound = true;
|
||||
else
|
||||
hasError = true;
|
||||
if (err instanceof NotFoundError) hasNotFound = true;
|
||||
else hasError = true;
|
||||
}
|
||||
expect(ops.expect.error ?? false).toBe(hasError);
|
||||
expect(ops.expect.notfound ?? false).toBe(hasNotFound);
|
||||
@@ -67,36 +63,30 @@ export function testSource(ops: TestSourceOptions) {
|
||||
|
||||
if (ops.types.includes('standard')) {
|
||||
it(`standard`, async () => {
|
||||
const providers = makeBaseProviders()
|
||||
.addSource(ops.source)
|
||||
.build();
|
||||
const providers = makeBaseProviders().addSource(ops.source).build();
|
||||
await runTest(providers);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (ops.types.includes('ip:standard')) {
|
||||
it(`standard:ip`, async () => {
|
||||
const providers = makeBaseProviders()
|
||||
.addSource(ops.source)
|
||||
.enableConsistentIpForRequests()
|
||||
.build();
|
||||
const providers = makeBaseProviders().addSource(ops.source).enableConsistentIpForRequests().build();
|
||||
await runTest(providers);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (ops.types.includes('proxied')) {
|
||||
it(`proxied`, async () => {
|
||||
if (!process.env.MOVIE_WEB_PROXY_URL)
|
||||
throw new Error("Cant use proxied test without setting MOVIE_WEB_PROXY_URL env");
|
||||
throw new Error('Cant use proxied test without setting MOVIE_WEB_PROXY_URL env');
|
||||
const providers = makeBaseProviders()
|
||||
.addSource(ops.source)
|
||||
.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch))
|
||||
.build();
|
||||
await runTest(providers);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import { testSource } from "./providerUtils";
|
||||
import { lookmovieScraper } from "@/providers/sources/lookmovie";
|
||||
import { testMedia } from "./testMedia";
|
||||
import { showboxScraper } from "@/providers/sources/showbox";
|
||||
import { testSource } from './providerUtils';
|
||||
import { lookmovieScraper } from '@/providers/sources/lookmovie';
|
||||
import { testMedia } from './testMedia';
|
||||
import { showboxScraper } from '@/providers/sources/showbox';
|
||||
import dotenv from 'dotenv';
|
||||
import { flixhqScraper } from "@/providers/sources/flixhq";
|
||||
import { goMoviesScraper } from "@/providers/sources/gomovies";
|
||||
import { smashyStreamScraper } from "@/providers/sources/smashystream";
|
||||
import { vidsrcScraper } from "@/providers/sources/vidsrc";
|
||||
import { vidSrcToScraper } from "@/providers/sources/vidsrcto";
|
||||
import { zoechipScraper } from "@/providers/sources/zoechip";
|
||||
import { remotestreamScraper } from "@/providers/sources/remotestream";
|
||||
import { flixhqScraper } from '@/providers/sources/flixhq';
|
||||
import { goMoviesScraper } from '@/providers/sources/gomovies';
|
||||
import { smashyStreamScraper } from '@/providers/sources/smashystream';
|
||||
import { vidsrcScraper } from '@/providers/sources/vidsrc';
|
||||
import { vidSrcToScraper } from '@/providers/sources/vidsrcto';
|
||||
import { zoechipScraper } from '@/providers/sources/zoechip';
|
||||
import { remotestreamScraper } from '@/providers/sources/remotestream';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -19,8 +19,8 @@ testSource({
|
||||
types: ['ip:standard'],
|
||||
expect: {
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: showboxScraper,
|
||||
@@ -28,8 +28,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: flixhqScraper,
|
||||
@@ -37,8 +37,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: goMoviesScraper,
|
||||
@@ -46,8 +46,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: smashyStreamScraper,
|
||||
@@ -55,8 +55,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: vidsrcScraper,
|
||||
@@ -64,8 +64,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: vidSrcToScraper,
|
||||
@@ -73,8 +73,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 2,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: zoechipScraper,
|
||||
@@ -82,8 +82,8 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
embeds: 3,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
testSource({
|
||||
source: remotestreamScraper,
|
||||
@@ -91,5 +91,5 @@ testSource({
|
||||
types: ['standard', 'proxied'],
|
||||
expect: {
|
||||
streams: 1,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { ScrapeMedia } from "@/entrypoint/utils/media";
|
||||
import { ScrapeMedia } from '@/entrypoint/utils/media';
|
||||
|
||||
function makeMedia(media: ScrapeMedia): ScrapeMedia {
|
||||
return media;
|
||||
@@ -6,9 +6,9 @@ function makeMedia(media: ScrapeMedia): ScrapeMedia {
|
||||
|
||||
export const testMedia = {
|
||||
arcane: makeMedia({
|
||||
type: "show",
|
||||
title: "Arcane",
|
||||
tmdbId: "94605",
|
||||
type: 'show',
|
||||
title: 'Arcane',
|
||||
tmdbId: '94605',
|
||||
releaseYear: 2021,
|
||||
episode: {
|
||||
number: 1,
|
||||
@@ -18,13 +18,13 @@ export const testMedia = {
|
||||
number: 1,
|
||||
tmdbId: '134187',
|
||||
},
|
||||
imdbId: 'tt11126994'
|
||||
imdbId: 'tt11126994',
|
||||
}),
|
||||
hamilton: makeMedia({
|
||||
type: 'movie',
|
||||
tmdbId: '556574',
|
||||
imdbId: 'tt8503618',
|
||||
releaseYear: 2020,
|
||||
title: 'Hamilton'
|
||||
})
|
||||
}
|
||||
title: 'Hamilton',
|
||||
}),
|
||||
};
|
||||
|
@@ -1,39 +1,39 @@
|
||||
import { serializeBody } from "@/fetchers/body";
|
||||
import FormData from "form-data";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { serializeBody } from '@/fetchers/body';
|
||||
import FormData from 'form-data';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe("serializeBody()", () => {
|
||||
describe('serializeBody()', () => {
|
||||
it('should work with standard text', () => {
|
||||
expect(serializeBody("hello world")).toEqual({
|
||||
expect(serializeBody('hello world')).toEqual({
|
||||
headers: {},
|
||||
body: "hello world"
|
||||
})
|
||||
})
|
||||
body: 'hello world',
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with objects', () => {
|
||||
expect(serializeBody({ hello: "world", a: 42 })).toEqual({
|
||||
expect(serializeBody({ hello: 'world', a: 42 })).toEqual({
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ hello: "world", a: 42 })
|
||||
})
|
||||
})
|
||||
body: JSON.stringify({ hello: 'world', a: 42 }),
|
||||
});
|
||||
});
|
||||
|
||||
it('should work x-www-form-urlencoded', () => {
|
||||
const obj = new URLSearchParams()
|
||||
obj.set("a", "b");
|
||||
const obj = new URLSearchParams();
|
||||
obj.set('a', 'b');
|
||||
expect(serializeBody(obj)).toEqual({
|
||||
headers: {},
|
||||
body: obj
|
||||
})
|
||||
})
|
||||
|
||||
body: obj,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work multipart/form-data', () => {
|
||||
const obj = new FormData()
|
||||
obj.append("a", "b");
|
||||
const obj = new FormData();
|
||||
obj.append('a', 'b');
|
||||
expect(serializeBody(obj)).toEqual({
|
||||
headers: {},
|
||||
body: obj
|
||||
})
|
||||
})
|
||||
})
|
||||
body: obj,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,48 +1,62 @@
|
||||
import { makeFullUrl } from "@/fetchers/common";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { makeFullUrl } from '@/fetchers/common';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe("makeFullUrl()", () => {
|
||||
describe('makeFullUrl()', () => {
|
||||
it('should pass normal url if no options', () => {
|
||||
expect(makeFullUrl('https://example.com/hello/world')).toEqual("https://example.com/hello/world")
|
||||
expect(makeFullUrl('https://example.com/hello/world?a=b')).toEqual("https://example.com/hello/world?a=b")
|
||||
expect(makeFullUrl('https://example.com/hello/world?a=b#hello')).toEqual("https://example.com/hello/world?a=b#hello")
|
||||
expect(makeFullUrl('https://example.com/hello/world#hello')).toEqual("https://example.com/hello/world#hello")
|
||||
})
|
||||
expect(makeFullUrl('https://example.com/hello/world')).toEqual('https://example.com/hello/world');
|
||||
expect(makeFullUrl('https://example.com/hello/world?a=b')).toEqual('https://example.com/hello/world?a=b');
|
||||
expect(makeFullUrl('https://example.com/hello/world?a=b#hello')).toEqual(
|
||||
'https://example.com/hello/world?a=b#hello',
|
||||
);
|
||||
expect(makeFullUrl('https://example.com/hello/world#hello')).toEqual('https://example.com/hello/world#hello');
|
||||
});
|
||||
|
||||
it('should append baseurl correctly', () => {
|
||||
const correctResult = "https://example.com/hello/world";
|
||||
expect(makeFullUrl(correctResult, { baseUrl: '' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult)
|
||||
expect(makeFullUrl('world?a=b', { baseUrl: 'https://example.com/hello' })).toEqual("https://example.com/hello/world?a=b")
|
||||
})
|
||||
const correctResult = 'https://example.com/hello/world';
|
||||
expect(makeFullUrl(correctResult, { baseUrl: '' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('/hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com/' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('hello/world', { baseUrl: 'https://example.com' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('/world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello/' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('world', { baseUrl: 'https://example.com/hello' })).toEqual(correctResult);
|
||||
expect(makeFullUrl('world?a=b', { baseUrl: 'https://example.com/hello' })).toEqual(
|
||||
'https://example.com/hello/world?a=b',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw with invalid baseurl combinations', () => {
|
||||
expect(() => makeFullUrl('example.com/hello/world', { baseUrl: '' })).toThrowError()
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'example.com' })).toThrowError()
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError()
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError()
|
||||
})
|
||||
expect(() => makeFullUrl('example.com/hello/world', { baseUrl: '' })).toThrowError();
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'example.com' })).toThrowError();
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError();
|
||||
expect(() => makeFullUrl('/hello/world', { baseUrl: 'tcp://example.com' })).toThrowError();
|
||||
});
|
||||
|
||||
it('should add/merge query parameters', () => {
|
||||
expect(makeFullUrl('https://example.com/hello/world', { query: { a: 'b' } })).toEqual("https://example.com/hello/world?a=b")
|
||||
expect(makeFullUrl('https://example.com/hello/world/', { query: { a: 'b' } })).toEqual("https://example.com/hello/world/?a=b")
|
||||
expect(makeFullUrl('https://example.com', { query: { a: 'b' } })).toEqual("https://example.com/?a=b")
|
||||
expect(makeFullUrl('https://example.com/', { query: { a: 'b' } })).toEqual("https://example.com/?a=b")
|
||||
expect(makeFullUrl('https://example.com/hello/world', { query: { a: 'b' } })).toEqual(
|
||||
'https://example.com/hello/world?a=b',
|
||||
);
|
||||
expect(makeFullUrl('https://example.com/hello/world/', { query: { a: 'b' } })).toEqual(
|
||||
'https://example.com/hello/world/?a=b',
|
||||
);
|
||||
expect(makeFullUrl('https://example.com', { query: { a: 'b' } })).toEqual('https://example.com/?a=b');
|
||||
expect(makeFullUrl('https://example.com/', { query: { a: 'b' } })).toEqual('https://example.com/?a=b');
|
||||
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: { a: 'b' } })).toEqual(
|
||||
'https://example.com/hello/world?c=d&a=b',
|
||||
);
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: {} })).toEqual(
|
||||
'https://example.com/hello/world?c=d',
|
||||
);
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d')).toEqual('https://example.com/hello/world?c=d');
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', {})).toEqual('https://example.com/hello/world?c=d');
|
||||
});
|
||||
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: { a: 'b' } })).toEqual("https://example.com/hello/world?c=d&a=b")
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', { query: {} })).toEqual("https://example.com/hello/world?c=d")
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d')).toEqual("https://example.com/hello/world?c=d")
|
||||
expect(makeFullUrl('https://example.com/hello/world?c=d', {})).toEqual("https://example.com/hello/world?c=d")
|
||||
})
|
||||
|
||||
it('should work with a mix of multiple options', () => {
|
||||
expect(makeFullUrl('/hello/world?c=d', { baseUrl: 'https://example.com/', query: { a: 'b' } })).toEqual("https://example.com/hello/world?c=d&a=b")
|
||||
})
|
||||
})
|
||||
expect(makeFullUrl('/hello/world?c=d', { baseUrl: 'https://example.com/', query: { a: 'b' } })).toEqual(
|
||||
'https://example.com/hello/world?c=d&a=b',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -1,138 +1,148 @@
|
||||
import { makeSimpleProxyFetcher } from "@/fetchers/simpleProxy";
|
||||
import { DefaultedFetcherOptions, FetcherOptions } from "@/fetchers/types";
|
||||
import { Headers } from "node-fetch";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { makeSimpleProxyFetcher } from '@/fetchers/simpleProxy';
|
||||
import { DefaultedFetcherOptions, FetcherOptions } from '@/fetchers/types';
|
||||
import { Headers } from 'node-fetch';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
describe("makeSimpleProxyFetcher()", () => {
|
||||
describe('makeSimpleProxyFetcher()', () => {
|
||||
const fetch = vi.fn();
|
||||
const fetcher = makeSimpleProxyFetcher("https://example.com/proxy", fetch);
|
||||
const fetcher = makeSimpleProxyFetcher('https://example.com/proxy', fetch);
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function setResult(type: "text" | "json", value: any) {
|
||||
if (type === 'text') return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
"content-type": "text/plain",
|
||||
}),
|
||||
status: 204,
|
||||
url: "test123",
|
||||
text() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
if (type === 'json') return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
"content-type": "application/json",
|
||||
}),
|
||||
status: 204,
|
||||
url: "test123",
|
||||
json() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
function setResult(type: 'text' | 'json', value: any) {
|
||||
if (type === 'text')
|
||||
return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
'content-type': 'text/plain',
|
||||
}),
|
||||
status: 204,
|
||||
url: 'test123',
|
||||
text() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
if (type === 'json')
|
||||
return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
'content-type': 'application/json',
|
||||
}),
|
||||
status: 204,
|
||||
url: 'test123',
|
||||
json() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function expectFetchCall(ops: { inputUrl: string, input: DefaultedFetcherOptions, outputUrl?: string, output: any, outputBody: any }) {
|
||||
function expectFetchCall(ops: {
|
||||
inputUrl: string;
|
||||
input: DefaultedFetcherOptions;
|
||||
outputUrl?: string;
|
||||
output: any;
|
||||
outputBody: any;
|
||||
}) {
|
||||
const prom = fetcher(ops.inputUrl, ops.input);
|
||||
expect((async () => (await prom).body)()).resolves.toEqual(ops.outputBody);
|
||||
expect((async () => (await prom).headers.entries())()).resolves.toEqual((new Headers()).entries());
|
||||
expect((async () => Array.from((await prom).headers.entries()))()).resolves.toEqual(
|
||||
Array.from(new Headers().entries()),
|
||||
);
|
||||
expect((async () => (await prom).statusCode)()).resolves.toEqual(204);
|
||||
expect((async () => (await prom).finalUrl)()).resolves.toEqual("test123");
|
||||
expect((async () => (await prom).finalUrl)()).resolves.toEqual('test123');
|
||||
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
|
||||
vi.clearAllMocks();
|
||||
}
|
||||
|
||||
it('should pass options through', () => {
|
||||
setResult("text", "hello world");
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
query: {},
|
||||
readHeaders: [],
|
||||
headers: {
|
||||
"X-Hello": "world",
|
||||
'X-Hello': 'world',
|
||||
},
|
||||
},
|
||||
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-Hello": "world",
|
||||
'X-Hello': 'world',
|
||||
},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("text", "hello world");
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
readHeaders: [],
|
||||
query: {
|
||||
"a": 'b',
|
||||
}
|
||||
a: 'b',
|
||||
},
|
||||
},
|
||||
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/?a=b')}`,
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("text", "hello world");
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
query: {},
|
||||
readHeaders: [],
|
||||
headers: {},
|
||||
},
|
||||
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse response correctly', () => {
|
||||
setResult("text", "hello world");
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com/",
|
||||
inputUrl: 'https://google.com/',
|
||||
input: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
query: {},
|
||||
readHeaders: [],
|
||||
headers: {},
|
||||
},
|
||||
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
|
||||
output: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("json", { hello: 42 });
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('json', { hello: 42 });
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com/",
|
||||
inputUrl: 'https://google.com/',
|
||||
input: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
query: {},
|
||||
readHeaders: [],
|
||||
headers: {},
|
||||
},
|
||||
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
|
||||
output: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: { hello: 42 }
|
||||
})
|
||||
outputBody: { hello: 42 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { makeStandardFetcher } from "@/fetchers/standardFetch";
|
||||
import { DefaultedFetcherOptions } from "@/fetchers/types";
|
||||
import { Headers } from "node-fetch";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { makeStandardFetcher } from '@/fetchers/standardFetch';
|
||||
import { DefaultedFetcherOptions } from '@/fetchers/types';
|
||||
import { Headers } from 'node-fetch';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
describe("makeStandardFetcher()", () => {
|
||||
describe('makeStandardFetcher()', () => {
|
||||
const fetch = vi.fn();
|
||||
const fetcher = makeStandardFetcher(fetch);
|
||||
|
||||
@@ -11,129 +11,139 @@ describe("makeStandardFetcher()", () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function setResult(type: "text" | "json", value: any) {
|
||||
if (type === 'text') return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
"content-type": "text/plain",
|
||||
}),
|
||||
status: 204,
|
||||
url: "test123",
|
||||
text() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
if (type === 'json') return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
"content-type": "application/json",
|
||||
}),
|
||||
status: 204,
|
||||
url: "test123",
|
||||
json() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
function setResult(type: 'text' | 'json', value: any) {
|
||||
if (type === 'text')
|
||||
return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
'content-type': 'text/plain',
|
||||
}),
|
||||
status: 204,
|
||||
url: 'test123',
|
||||
text() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
if (type === 'json')
|
||||
return fetch.mockResolvedValueOnce({
|
||||
headers: new Headers({
|
||||
'content-type': 'application/json',
|
||||
}),
|
||||
status: 204,
|
||||
url: 'test123',
|
||||
json() {
|
||||
return Promise.resolve(value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function expectFetchCall(ops: { inputUrl: string, input: DefaultedFetcherOptions, outputUrl?: string, output: any, outputBody: any }) {
|
||||
function expectFetchCall(ops: {
|
||||
inputUrl: string;
|
||||
input: DefaultedFetcherOptions;
|
||||
outputUrl?: string;
|
||||
output: any;
|
||||
outputBody: any;
|
||||
}) {
|
||||
const prom = fetcher(ops.inputUrl, ops.input);
|
||||
expect((async () => (await prom).body)()).resolves.toEqual(ops.outputBody);
|
||||
expect((async () => (await prom).headers.entries())()).resolves.toEqual((new Headers()).entries());
|
||||
expect((async () => Array.from((await prom).headers.entries()))()).resolves.toEqual(
|
||||
Array.from(new Headers().entries()),
|
||||
);
|
||||
expect((async () => (await prom).statusCode)()).resolves.toEqual(204);
|
||||
expect((async () => (await prom).finalUrl)()).resolves.toEqual("test123");
|
||||
expect((async () => (await prom).finalUrl)()).resolves.toEqual('test123');
|
||||
expect(fetch).toBeCalledWith(ops.outputUrl ?? ops.inputUrl, ops.output);
|
||||
vi.clearAllMocks();
|
||||
}
|
||||
|
||||
it('should pass options through', () => {
|
||||
setResult("text", "hello world");
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
query: {},
|
||||
readHeaders: [],
|
||||
headers: {
|
||||
"X-Hello": "world",
|
||||
'X-Hello': 'world',
|
||||
},
|
||||
},
|
||||
outputUrl: "https://google.com/",
|
||||
outputUrl: 'https://google.com/',
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"X-Hello": "world",
|
||||
'X-Hello': 'world',
|
||||
},
|
||||
body: undefined,
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("text", "hello world");
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
readHeaders: [],
|
||||
query: {
|
||||
"a": 'b',
|
||||
}
|
||||
a: 'b',
|
||||
},
|
||||
},
|
||||
outputUrl: "https://google.com/?a=b",
|
||||
outputUrl: 'https://google.com/?a=b',
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("text", "hello world");
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com",
|
||||
inputUrl: 'https://google.com',
|
||||
input: {
|
||||
query: {},
|
||||
headers: {},
|
||||
readHeaders: [],
|
||||
method: "GET"
|
||||
method: 'GET',
|
||||
},
|
||||
outputUrl: "https://google.com/",
|
||||
outputUrl: 'https://google.com/',
|
||||
output: {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse response correctly', () => {
|
||||
setResult("text", "hello world");
|
||||
setResult('text', 'hello world');
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com/",
|
||||
inputUrl: 'https://google.com/',
|
||||
input: {
|
||||
query: {},
|
||||
headers: {},
|
||||
readHeaders: [],
|
||||
method: "POST"
|
||||
method: 'POST',
|
||||
},
|
||||
outputUrl: "https://google.com/",
|
||||
outputUrl: 'https://google.com/',
|
||||
output: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: "hello world"
|
||||
})
|
||||
setResult("json", { hello: 42 });
|
||||
outputBody: 'hello world',
|
||||
});
|
||||
setResult('json', { hello: 42 });
|
||||
expectFetchCall({
|
||||
inputUrl: "https://google.com/",
|
||||
inputUrl: 'https://google.com/',
|
||||
input: {
|
||||
query: {},
|
||||
headers: {},
|
||||
readHeaders: [],
|
||||
method: "POST"
|
||||
method: 'POST',
|
||||
},
|
||||
outputUrl: "https://google.com/",
|
||||
outputUrl: 'https://google.com/',
|
||||
output: {
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
},
|
||||
outputBody: { hello: 42 }
|
||||
})
|
||||
outputBody: { hello: 42 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -9,8 +9,8 @@ vi.mock('@/providers/all', () => mocks);
|
||||
|
||||
const features: FeatureMap = {
|
||||
requires: [],
|
||||
disallowed: []
|
||||
}
|
||||
disallowed: [],
|
||||
};
|
||||
|
||||
describe('getProviders()', () => {
|
||||
afterEach(() => {
|
||||
@@ -20,10 +20,12 @@ describe('getProviders()', () => {
|
||||
it('should return providers', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
|
||||
expect(getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toEqual({
|
||||
expect(
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toEqual({
|
||||
sources: [mockSources.sourceA, mockSources.sourceB],
|
||||
embeds: [mockEmbeds.embedD],
|
||||
});
|
||||
@@ -32,10 +34,12 @@ describe('getProviders()', () => {
|
||||
it('should filter out disabled providers', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedEDisabled]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceCDisabled, mockSources.sourceB]);
|
||||
expect(getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toEqual({
|
||||
expect(
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toEqual({
|
||||
sources: [mockSources.sourceA, mockSources.sourceB],
|
||||
embeds: [mockEmbeds.embedD],
|
||||
});
|
||||
@@ -44,46 +48,56 @@ describe('getProviders()', () => {
|
||||
it('should throw on duplicate ids in sources', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceAHigherRank, mockSources.sourceA, mockSources.sourceB]);
|
||||
expect(() => getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toThrowError();
|
||||
expect(() =>
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw on duplicate ids in embeds', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedDHigherRank, mockEmbeds.embedA]);
|
||||
mocks.gatherAllSources.mockReturnValue([]);
|
||||
expect(() => getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toThrowError();
|
||||
expect(() =>
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw on duplicate ids between sources and embeds', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedA]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
|
||||
expect(() => getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toThrowError();
|
||||
expect(() =>
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw on duplicate rank between sources and embeds', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedA]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
|
||||
expect(() => getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toThrowError();
|
||||
expect(() =>
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
it('should not throw with same rank between sources and embeds', () => {
|
||||
mocks.gatherAllEmbeds.mockReturnValue([mockEmbeds.embedD, mockEmbeds.embedHSameRankAsSourceA]);
|
||||
mocks.gatherAllSources.mockReturnValue([mockSources.sourceA, mockSources.sourceB]);
|
||||
expect(getProviders(features,{
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
})).toEqual({
|
||||
expect(
|
||||
getProviders(features, {
|
||||
embeds: getBuiltinEmbeds(),
|
||||
sources: getBuiltinSources(),
|
||||
}),
|
||||
).toEqual({
|
||||
sources: [mockSources.sourceA, mockSources.sourceB],
|
||||
embeds: [mockEmbeds.embedD, mockEmbeds.embedHSameRankAsSourceA],
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { FeatureMap, Flags, flags, flagsAllowedInFeatures } from "@/entrypoint/utils/targets";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { FeatureMap, Flags, flags, flagsAllowedInFeatures } from '@/entrypoint/utils/targets';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('flagsAllowedInFeatures()', () => {
|
||||
function checkFeatures(featureMap: FeatureMap, flags: Flags[], output: boolean) {
|
||||
@@ -7,71 +7,131 @@ describe('flagsAllowedInFeatures()', () => {
|
||||
}
|
||||
|
||||
it('should check required correctly', () => {
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: []
|
||||
}, [], true);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: []
|
||||
}, [flags.CORS_ALLOWED], true);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: []
|
||||
}, [], false);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
disallowed: []
|
||||
}, [flags.CORS_ALLOWED, flags.IP_LOCKED], true);
|
||||
checkFeatures({
|
||||
requires: [flags.IP_LOCKED],
|
||||
disallowed: []
|
||||
}, [flags.CORS_ALLOWED], false);
|
||||
checkFeatures({
|
||||
requires: [flags.IP_LOCKED],
|
||||
disallowed: []
|
||||
}, [], false);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [],
|
||||
},
|
||||
[],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [],
|
||||
},
|
||||
[flags.CORS_ALLOWED],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [],
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
disallowed: [],
|
||||
},
|
||||
[flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.IP_LOCKED],
|
||||
disallowed: [],
|
||||
},
|
||||
[flags.CORS_ALLOWED],
|
||||
false,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.IP_LOCKED],
|
||||
disallowed: [],
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should check disallowed correctly', () => {
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: []
|
||||
}, [], true);
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED]
|
||||
}, [], true);
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED]
|
||||
}, [flags.CORS_ALLOWED], false);
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED]
|
||||
}, [flags.IP_LOCKED], true);
|
||||
checkFeatures({
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED, flags.IP_LOCKED]
|
||||
}, [flags.CORS_ALLOWED], false);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [],
|
||||
},
|
||||
[],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED],
|
||||
},
|
||||
[],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED],
|
||||
},
|
||||
[flags.CORS_ALLOWED],
|
||||
false,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED],
|
||||
},
|
||||
[flags.IP_LOCKED],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [],
|
||||
disallowed: [flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
},
|
||||
[flags.CORS_ALLOWED],
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should pass mixed tests', () => {
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED]
|
||||
}, [], false);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED]
|
||||
}, [flags.CORS_ALLOWED], true);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED]
|
||||
}, [flags.IP_LOCKED], false);
|
||||
checkFeatures({
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED]
|
||||
}, [flags.IP_LOCKED, flags.CORS_ALLOWED], false);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED],
|
||||
},
|
||||
[],
|
||||
false,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED],
|
||||
},
|
||||
[flags.CORS_ALLOWED],
|
||||
true,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED],
|
||||
},
|
||||
[flags.IP_LOCKED],
|
||||
false,
|
||||
);
|
||||
checkFeatures(
|
||||
{
|
||||
requires: [flags.CORS_ALLOWED],
|
||||
disallowed: [flags.IP_LOCKED],
|
||||
},
|
||||
[flags.IP_LOCKED, flags.CORS_ALLOWED],
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import { reorderOnIdList } from "@/utils/list";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { reorderOnIdList } from '@/utils/list';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
function list(def: string) {
|
||||
return def.split(",").map(v=>({
|
||||
return def.split(',').map((v) => ({
|
||||
rank: parseInt(v),
|
||||
id: v,
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
function expectListToEqual(l1: ReturnType<typeof list>, l2: ReturnType<typeof list>) {
|
||||
function flatten(l: ReturnType<typeof list>) {
|
||||
return l.map(v=>v.id).join(",");
|
||||
return l.map((v) => v.id).join(',');
|
||||
}
|
||||
expect(flatten(l1)).toEqual(flatten(l2));
|
||||
}
|
||||
@@ -18,36 +18,36 @@ function expectListToEqual(l1: ReturnType<typeof list>, l2: ReturnType<typeof li
|
||||
describe('reorderOnIdList()', () => {
|
||||
it('should reorder based on rank', () => {
|
||||
const l = list('2,1,4,3');
|
||||
const sortedList = list('4,3,2,1')
|
||||
const sortedList = list('4,3,2,1');
|
||||
expectListToEqual(reorderOnIdList([], l), sortedList);
|
||||
});
|
||||
|
||||
it('should work with empty input', () => {
|
||||
expectListToEqual(reorderOnIdList([], []), []);
|
||||
});
|
||||
|
||||
|
||||
it('should reorder based on id list', () => {
|
||||
const l = list('4,2,1,3');
|
||||
const sortedList = list('4,3,2,1')
|
||||
expectListToEqual(reorderOnIdList(["4","3","2","1"], l), sortedList);
|
||||
const sortedList = list('4,3,2,1');
|
||||
expectListToEqual(reorderOnIdList(['4', '3', '2', '1'], l), sortedList);
|
||||
});
|
||||
|
||||
|
||||
it('should reorder based on id list and rank second', () => {
|
||||
const l = list('4,2,1,3');
|
||||
const sortedList = list('4,3,2,1')
|
||||
expectListToEqual(reorderOnIdList(["4","3"], l), sortedList);
|
||||
const sortedList = list('4,3,2,1');
|
||||
expectListToEqual(reorderOnIdList(['4', '3'], l), sortedList);
|
||||
});
|
||||
|
||||
|
||||
it('should work with only one item', () => {
|
||||
const l = list('1');
|
||||
const sortedList = list('1')
|
||||
expectListToEqual(reorderOnIdList(["1"], l), sortedList);
|
||||
const sortedList = list('1');
|
||||
expectListToEqual(reorderOnIdList(['1'], l), sortedList);
|
||||
expectListToEqual(reorderOnIdList([], l), sortedList);
|
||||
});
|
||||
|
||||
it('should not affect original list', () => {
|
||||
const l = list('4,3,2,1');
|
||||
const unsortedList = list('4,3,2,1')
|
||||
const unsortedList = list('4,3,2,1');
|
||||
reorderOnIdList([], l);
|
||||
expectListToEqual(l, unsortedList);
|
||||
});
|
||||
|
@@ -1,65 +1,71 @@
|
||||
import { makeStandardFetcher } from "@/fetchers/standardFetch";
|
||||
import { makeProviders } from "@/main/builder";
|
||||
import { targets } from "@/main/targets";
|
||||
import { isValidStream } from "@/utils/valid";
|
||||
import fetch from "node-fetch";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { isValidStream } from '@/utils/valid';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('isValidStream()', () => {
|
||||
it('should pass valid streams', () => {
|
||||
expect(isValidStream({
|
||||
type: "file",
|
||||
id: "a",
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {
|
||||
"1080": {
|
||||
type: "mp4",
|
||||
url: "hello-world"
|
||||
}
|
||||
}
|
||||
})).toBe(true);
|
||||
expect(isValidStream({
|
||||
type: "hls",
|
||||
id: "a",
|
||||
flags: [],
|
||||
captions: [],
|
||||
playlist: "hello-world"
|
||||
})).toBe(true);
|
||||
expect(
|
||||
isValidStream({
|
||||
type: 'file',
|
||||
id: 'a',
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {
|
||||
'1080': {
|
||||
type: 'mp4',
|
||||
url: 'hello-world',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isValidStream({
|
||||
type: 'hls',
|
||||
id: 'a',
|
||||
flags: [],
|
||||
captions: [],
|
||||
playlist: 'hello-world',
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should detect empty qualities', () => {
|
||||
expect(isValidStream({
|
||||
type: "file",
|
||||
id: "a",
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {}
|
||||
})).toBe(false);
|
||||
expect(
|
||||
isValidStream({
|
||||
type: 'file',
|
||||
id: 'a',
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {},
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should detect empty stream urls', () => {
|
||||
expect(isValidStream({
|
||||
type: "file",
|
||||
id: "a",
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {
|
||||
"1080": {
|
||||
type: "mp4",
|
||||
url: "",
|
||||
}
|
||||
}
|
||||
})).toBe(false);
|
||||
expect(
|
||||
isValidStream({
|
||||
type: 'file',
|
||||
id: 'a',
|
||||
flags: [],
|
||||
captions: [],
|
||||
qualities: {
|
||||
'1080': {
|
||||
type: 'mp4',
|
||||
url: '',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should detect emtpy HLS playlists', () => {
|
||||
expect(isValidStream({
|
||||
type: "hls",
|
||||
id: "a",
|
||||
flags: [],
|
||||
captions: [],
|
||||
playlist: "",
|
||||
})).toBe(false);
|
||||
expect(
|
||||
isValidStream({
|
||||
type: 'hls',
|
||||
id: 'a',
|
||||
flags: [],
|
||||
captions: [],
|
||||
playlist: '',
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@@ -179,7 +179,11 @@ async function runCommandLine() {
|
||||
}
|
||||
|
||||
if (process.argv.length === 2) {
|
||||
runQuestions().catch(() => console.error('Exited.'));
|
||||
runQuestions()
|
||||
.catch(() => console.error('Exited.'))
|
||||
.finally(() => process.exit(0));
|
||||
} else {
|
||||
runCommandLine().catch(() => console.error('Exited.'));
|
||||
runCommandLine()
|
||||
.catch(() => console.error('Exited.'))
|
||||
.finally(() => process.exit(0));
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ async function runBrowserScraping(
|
||||
source: MetaOutput,
|
||||
options: CommandLineArguments,
|
||||
) {
|
||||
if (!existsSync(join(__dirname, '../../lib/index.mjs')))
|
||||
if (!existsSync(join(__dirname, '../../lib/index.js')))
|
||||
throw new Error('Please compile before running cli in browser mode');
|
||||
const config = getConfig();
|
||||
if (!config.proxyUrl)
|
||||
@@ -37,13 +37,15 @@ async function runBrowserScraping(
|
||||
root,
|
||||
});
|
||||
browser = await puppeteer.launch({
|
||||
headless: 'new',
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
// This is the dev cli, so we can use console.log
|
||||
// eslint-disable-next-line no-console
|
||||
page.on('console', (message) => console.log(`${message.type().slice(0, 3).toUpperCase()} ${message.text()}`));
|
||||
|
||||
if (!server.resolvedUrls?.local.length) throw new Error('Server did not start');
|
||||
await page.goto(server.resolvedUrls.local[0]);
|
||||
await page.waitForFunction('!!window.scrape', { timeout: 5000 });
|
||||
|
||||
|
@@ -23,12 +23,16 @@ import { fileMoonScraper } from './embeds/filemoon';
|
||||
import { ridooScraper } from './embeds/ridoo';
|
||||
import { smashyStreamDScraper } from './embeds/smashystream/dued';
|
||||
import { smashyStreamFScraper } from './embeds/smashystream/video1';
|
||||
import { streamtapeScraper } from './embeds/streamtape';
|
||||
import { streamvidScraper } from './embeds/streamvid';
|
||||
import { vidCloudScraper } from './embeds/vidcloud';
|
||||
import { vidplayScraper } from './embeds/vidplay';
|
||||
import { voeScraper } from './embeds/voe';
|
||||
import { wootlyScraper } from './embeds/wootly';
|
||||
import { goojaraScraper } from './sources/goojara';
|
||||
import { hdRezkaScraper } from './sources/hdrezka';
|
||||
import { nepuScraper } from './sources/nepu';
|
||||
import { primewireScraper } from './sources/primewire';
|
||||
import { ridooMoviesScraper } from './sources/ridomovies';
|
||||
import { smashyStreamScraper } from './sources/smashystream';
|
||||
import { vidSrcToScraper } from './sources/vidsrcto';
|
||||
@@ -50,6 +54,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
||||
nepuScraper,
|
||||
goojaraScraper,
|
||||
hdRezkaScraper,
|
||||
primewireScraper,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -74,5 +79,8 @@ export function gatherAllEmbeds(): Array<Embed> {
|
||||
vidplayScraper,
|
||||
wootlyScraper,
|
||||
doodScraper,
|
||||
streamvidScraper,
|
||||
voeScraper,
|
||||
streamtapeScraper,
|
||||
];
|
||||
}
|
||||
|
@@ -3,15 +3,20 @@ import { customAlphabet } from 'nanoid';
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
const nanoid = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 10);
|
||||
const baseUrl = 'https://d000d.com';
|
||||
|
||||
export const doodScraper = makeEmbed({
|
||||
id: 'dood',
|
||||
name: 'dood',
|
||||
rank: 173,
|
||||
async scrape(ctx) {
|
||||
const baseUrl = 'https://d0000d.com';
|
||||
let url = ctx.url;
|
||||
if (ctx.url.includes('primewire')) {
|
||||
const request = await ctx.proxiedFetcher.full(ctx.url);
|
||||
url = request.finalUrl;
|
||||
}
|
||||
|
||||
const id = ctx.url.split('/d/')[1] || ctx.url.split('/e/')[1];
|
||||
const id = url.split('/d/')[1] || url.split('/e/')[1];
|
||||
|
||||
const doodData = await ctx.proxiedFetcher<string>(`/e/${id}`, {
|
||||
method: 'GET',
|
||||
@@ -46,7 +51,7 @@ export const doodScraper = makeEmbed({
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
Referer: 'https://d0000d.com/',
|
||||
Referer: baseUrl,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { load } from 'cheerio';
|
||||
import { unpack } from 'unpacker';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
@@ -19,7 +20,8 @@ export const fileMoonScraper = makeEmbed({
|
||||
referer: ctx.url,
|
||||
},
|
||||
});
|
||||
const evalCode = embedRes.match(evalCodeRegex);
|
||||
const embedHtml = load(embedRes);
|
||||
const evalCode = embedHtml('script').text().match(evalCodeRegex);
|
||||
if (!evalCode) throw new Error('Failed to find eval code');
|
||||
const unpacked = unpack(evalCode[1]);
|
||||
const file = fileRegex.exec(unpacked);
|
||||
|
@@ -2,6 +2,7 @@ import * as unpacker from 'unpacker';
|
||||
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
const mixdropBase = 'https://mixdrop.ag';
|
||||
const packedRegex = /(eval\(function\(p,a,c,k,e,d\){.*{}\)\))/;
|
||||
const linkRegex = /MDCore\.wurl="(.*?)";/;
|
||||
|
||||
@@ -12,7 +13,16 @@ export const mixdropScraper = makeEmbed({
|
||||
async scrape(ctx) {
|
||||
// Example url: https://mixdrop.co/e/pkwrgp0pizgod0
|
||||
// Example url: https://mixdrop.vc/e/pkwrgp0pizgod0
|
||||
const streamRes = await ctx.proxiedFetcher<string>(ctx.url);
|
||||
let embedUrl = ctx.url;
|
||||
if (ctx.url.includes('primewire')) embedUrl = (await ctx.fetcher.full(ctx.url)).finalUrl;
|
||||
const embedId = new URL(embedUrl).pathname.split('/')[2];
|
||||
// constructing the url because many times mixdrop.co is returned which does not work
|
||||
// this also handels the case where preview page urls are returned
|
||||
// Example: https://mixdrop.vc/f/pkwrgp0pizgod0
|
||||
// these don't have the packed code
|
||||
const streamRes = await ctx.proxiedFetcher<string>(`/e/${embedId}`, {
|
||||
baseUrl: mixdropBase,
|
||||
});
|
||||
const packed = streamRes.match(packedRegex);
|
||||
|
||||
// MixDrop uses a queue system for embeds
|
||||
@@ -45,7 +55,7 @@ export const mixdropScraper = makeEmbed({
|
||||
url: url.startsWith('http') ? url : `https:${url}`, // URLs don't always start with the protocol
|
||||
headers: {
|
||||
// MixDrop requires this header on all streams
|
||||
Referer: 'https://mixdrop.co/',
|
||||
Referer: mixdropBase,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -146,13 +146,16 @@ export const streamsbScraper = makeEmbed({
|
||||
|
||||
ctx.progress(80);
|
||||
|
||||
const qualities = dls.reduce((a, v) => {
|
||||
a[v.quality] = {
|
||||
type: 'mp4',
|
||||
url: v.url as string,
|
||||
};
|
||||
return a;
|
||||
}, {} as Record<string, StreamFile>);
|
||||
const qualities = dls.reduce(
|
||||
(a, v) => {
|
||||
a[v.quality] = {
|
||||
type: 'mp4',
|
||||
url: v.url as string,
|
||||
};
|
||||
return a;
|
||||
},
|
||||
{} as Record<string, StreamFile>,
|
||||
);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
|
39
src/providers/embeds/streamtape.ts
Normal file
39
src/providers/embeds/streamtape.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
export const streamtapeScraper = makeEmbed({
|
||||
id: 'streamtape',
|
||||
name: 'Streamtape',
|
||||
rank: 160,
|
||||
async scrape(ctx) {
|
||||
const embed = await ctx.proxiedFetcher<string>(ctx.url);
|
||||
|
||||
const match = embed.match(/robotlink'\).innerHTML = (.*)'/);
|
||||
if (!match) throw new Error('No match found');
|
||||
|
||||
const [fh, sh] = match?.[1]?.split("+ ('") ?? [];
|
||||
if (!fh || !sh) throw new Error('No match found');
|
||||
|
||||
const url = `https:${fh?.replace(/'/g, '').trim()}${sh?.substring(3).trim()}`;
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
flags: [flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
captions: [],
|
||||
qualities: {
|
||||
unknown: {
|
||||
type: 'mp4',
|
||||
url,
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
Referer: 'https://streamtape.com',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
36
src/providers/embeds/streamvid.ts
Normal file
36
src/providers/embeds/streamvid.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as unpacker from 'unpacker';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
const packedRegex = /(eval\(function\(p,a,c,k,e,d\).*\)\)\))/;
|
||||
const linkRegex = /src:"(https:\/\/[^"]+)"/;
|
||||
|
||||
export const streamvidScraper = makeEmbed({
|
||||
id: 'streamvid',
|
||||
name: 'Streamvid',
|
||||
rank: 215,
|
||||
async scrape(ctx) {
|
||||
// Example url: https://streamvid.net/fu1jaf96vofx
|
||||
const streamRes = await ctx.proxiedFetcher<string>(ctx.url);
|
||||
const packed = streamRes.match(packedRegex);
|
||||
|
||||
if (!packed) throw new Error('streamvid packed not found');
|
||||
|
||||
const unpacked = unpacker.unpack(packed[1]);
|
||||
const link = unpacked.match(linkRegex);
|
||||
|
||||
if (!link) throw new Error('streamvid link not found');
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: link[1],
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
@@ -60,6 +60,7 @@ export const upcloudScraper = makeEmbed({
|
||||
id: 'upcloud',
|
||||
name: 'UpCloud',
|
||||
rank: 200,
|
||||
disabled: true,
|
||||
async scrape(ctx) {
|
||||
// Example url: https://dokicloud.one/embed-4/{id}?z=
|
||||
const parsedUrl = new URL(ctx.url.replace('embed-5', 'embed-4'));
|
||||
|
@@ -6,6 +6,7 @@ export const vidCloudScraper = makeEmbed({
|
||||
id: 'vidcloud',
|
||||
name: 'VidCloud',
|
||||
rank: 201,
|
||||
disabled: true,
|
||||
async scrape(ctx) {
|
||||
// Both vidcloud and upcloud have the same embed domain (rabbitstream.net)
|
||||
const result = await upcloudScraper.scrape(ctx);
|
||||
|
@@ -3,7 +3,7 @@ import { makeEmbed } from '@/providers/base';
|
||||
import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions';
|
||||
|
||||
import { getFileUrl } from './common';
|
||||
import { SubtitleResult, VidplaySourceResponse } from './types';
|
||||
import { SubtitleResult, ThumbnailTrack, VidplaySourceResponse } from './types';
|
||||
|
||||
export const vidplayScraper = makeEmbed({
|
||||
id: 'vidplay',
|
||||
@@ -18,6 +18,15 @@ export const vidplayScraper = makeEmbed({
|
||||
});
|
||||
if (typeof fileUrlRes.result === 'number') throw new Error('File not found');
|
||||
const source = fileUrlRes.result.sources[0].file;
|
||||
const thumbnailSource = fileUrlRes.result.tracks.find((track) => track.kind === 'thumbnails');
|
||||
|
||||
let thumbnailTrack: ThumbnailTrack | undefined;
|
||||
if (thumbnailSource) {
|
||||
thumbnailTrack = {
|
||||
type: 'vtt',
|
||||
url: thumbnailSource.file,
|
||||
};
|
||||
}
|
||||
|
||||
const url = new URL(ctx.url);
|
||||
const subtitlesLink = url.searchParams.get('sub.info');
|
||||
@@ -47,6 +56,7 @@ export const vidplayScraper = makeEmbed({
|
||||
playlist: source,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions,
|
||||
thumbnailTrack,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@@ -3,10 +3,10 @@ export type VidplaySourceResponse = {
|
||||
| {
|
||||
sources: {
|
||||
file: string;
|
||||
tracks: {
|
||||
file: string;
|
||||
kind: string;
|
||||
}[];
|
||||
}[];
|
||||
tracks: {
|
||||
file: string;
|
||||
kind: string;
|
||||
}[];
|
||||
}
|
||||
| number;
|
||||
@@ -17,3 +17,8 @@ export type SubtitleResult = {
|
||||
label: string;
|
||||
kind: string;
|
||||
}[];
|
||||
|
||||
export type ThumbnailTrack = {
|
||||
type: 'vtt';
|
||||
url: string;
|
||||
};
|
||||
|
33
src/providers/embeds/voe.ts
Normal file
33
src/providers/embeds/voe.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
|
||||
const linkRegex = /'hls': ?'(http.*?)',/;
|
||||
|
||||
export const voeScraper = makeEmbed({
|
||||
id: 'voe',
|
||||
name: 'voe.sx',
|
||||
rank: 180,
|
||||
async scrape(ctx) {
|
||||
const embed = await ctx.proxiedFetcher<string>(ctx.url);
|
||||
|
||||
const playerSrc = embed.match(linkRegex) ?? [];
|
||||
|
||||
const streamUrl = playerSrc[1];
|
||||
if (!streamUrl) throw new Error('Stream url not found in embed code');
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
type: 'hls',
|
||||
id: 'primary',
|
||||
playlist: streamUrl,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
headers: {
|
||||
Referer: 'https://voe.sx',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
@@ -9,7 +9,7 @@ import { NotFoundError } from '@/utils/errors';
|
||||
export const flixhqScraper = makeSourcerer({
|
||||
id: 'flixhq',
|
||||
name: 'FlixHQ',
|
||||
rank: 100,
|
||||
rank: 61,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
disabled: true,
|
||||
async scrapeMovie(ctx) {
|
||||
|
@@ -2,7 +2,12 @@ import { load } from 'cheerio';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeSourcerer } from '@/providers/base';
|
||||
import { doodScraper } from '@/providers/embeds/dood';
|
||||
import { mixdropScraper } from '@/providers/embeds/mixdrop';
|
||||
import { upcloudScraper } from '@/providers/embeds/upcloud';
|
||||
import { upstreamScraper } from '@/providers/embeds/upstream';
|
||||
import { vidCloudScraper } from '@/providers/embeds/vidcloud';
|
||||
import { voeScraper } from '@/providers/embeds/voe';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
import { getSource } from './source';
|
||||
@@ -12,13 +17,12 @@ export const gomoviesBase = `https://gomovies.sx`;
|
||||
export const goMoviesScraper = makeSourcerer({
|
||||
id: 'gomovies',
|
||||
name: 'GOmovies',
|
||||
rank: 110,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
rank: 60,
|
||||
disabled: true,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
async scrapeShow(ctx) {
|
||||
const search = await ctx.proxiedFetcher<string>(`/ajax/search`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ keyword: ctx.media.title }),
|
||||
const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
@@ -26,12 +30,12 @@ export const goMoviesScraper = makeSourcerer({
|
||||
});
|
||||
|
||||
const searchPage = load(search);
|
||||
const mediaElements = searchPage('a.nav-item');
|
||||
const mediaElements = searchPage('div.film-detail');
|
||||
|
||||
const mediaData = mediaElements.toArray().map((movieEl) => {
|
||||
const name = searchPage(movieEl).find('h3.film-name')?.text();
|
||||
const year = searchPage(movieEl).find('div.film-infor span:first-of-type')?.text();
|
||||
const path = searchPage(movieEl).attr('href');
|
||||
const name = searchPage(movieEl).find('h2.film-name a')?.text();
|
||||
const year = searchPage(movieEl).find('span.fdi-item:first')?.text();
|
||||
const path = searchPage(movieEl).find('h2.film-name a').attr('href');
|
||||
return { name, year, path };
|
||||
});
|
||||
|
||||
@@ -89,21 +93,56 @@ export const goMoviesScraper = makeSourcerer({
|
||||
},
|
||||
});
|
||||
|
||||
const upcloudSource = await getSource(ctx, sources);
|
||||
const upcloudSource = await getSource(ctx, sources, 'upcloud');
|
||||
const vidcloudSource = await getSource(ctx, sources, 'vidcloud');
|
||||
const voeSource = await getSource(ctx, sources, 'voe');
|
||||
const doodSource = await getSource(ctx, sources, 'doodstream');
|
||||
const upstreamSource = await getSource(ctx, sources, 'upstream');
|
||||
const mixdropSource = await getSource(ctx, sources, 'mixdrop');
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: upcloudScraper.id,
|
||||
url: upcloudSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: vidCloudScraper.id,
|
||||
url: vidcloudSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: voeScraper.id,
|
||||
url: voeSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: doodScraper.id,
|
||||
url: doodSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: upstreamScraper.id,
|
||||
url: upstreamSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: mixdropScraper.id,
|
||||
url: mixdropSource?.link,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredEmbeds = embeds
|
||||
.filter((embed) => embed.url)
|
||||
.map((embed) => ({
|
||||
embedId: embed.embedId,
|
||||
url: embed.url as string,
|
||||
}));
|
||||
|
||||
if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.');
|
||||
|
||||
return {
|
||||
embeds: [
|
||||
{
|
||||
embedId: upcloudScraper.id,
|
||||
url: upcloudSource.link,
|
||||
},
|
||||
],
|
||||
embeds: filteredEmbeds,
|
||||
};
|
||||
},
|
||||
async scrapeMovie(ctx) {
|
||||
const search = await ctx.proxiedFetcher<string>(`ajax/search`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ keyword: ctx.media.title }),
|
||||
const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
@@ -111,12 +150,12 @@ export const goMoviesScraper = makeSourcerer({
|
||||
});
|
||||
|
||||
const searchPage = load(search);
|
||||
const mediaElements = searchPage('a.nav-item');
|
||||
const mediaElements = searchPage('div.film-detail');
|
||||
|
||||
const mediaData = mediaElements.toArray().map((movieEl) => {
|
||||
const name = searchPage(movieEl).find('h3.film-name')?.text();
|
||||
const year = searchPage(movieEl).find('div.film-infor span:first-of-type')?.text();
|
||||
const path = searchPage(movieEl).attr('href');
|
||||
const name = searchPage(movieEl).find('h2.film-name a')?.text();
|
||||
const year = searchPage(movieEl).find('span.fdi-item:first')?.text();
|
||||
const path = searchPage(movieEl).find('h2.film-name a').attr('href');
|
||||
return { name, year, path };
|
||||
});
|
||||
|
||||
@@ -136,15 +175,51 @@ export const goMoviesScraper = makeSourcerer({
|
||||
baseUrl: gomoviesBase,
|
||||
});
|
||||
|
||||
const upcloudSource = await getSource(ctx, sources);
|
||||
const upcloudSource = await getSource(ctx, sources, 'upcloud');
|
||||
const vidcloudSource = await getSource(ctx, sources, 'vidcloud');
|
||||
const voeSource = await getSource(ctx, sources, 'voe');
|
||||
const doodSource = await getSource(ctx, sources, 'doodstream');
|
||||
const upstreamSource = await getSource(ctx, sources, 'upstream');
|
||||
const mixdropSource = await getSource(ctx, sources, 'mixdrop');
|
||||
|
||||
const embeds = [
|
||||
{
|
||||
embedId: upcloudScraper.id,
|
||||
url: upcloudSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: vidCloudScraper.id,
|
||||
url: vidcloudSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: voeScraper.id,
|
||||
url: voeSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: doodScraper.id,
|
||||
url: doodSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: upstreamScraper.id,
|
||||
url: upstreamSource?.link,
|
||||
},
|
||||
{
|
||||
embedId: mixdropScraper.id,
|
||||
url: mixdropSource?.link,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredEmbeds = embeds
|
||||
.filter((embed) => embed.url)
|
||||
.map((embed) => ({
|
||||
embedId: embed.embedId,
|
||||
url: embed.url as string,
|
||||
}));
|
||||
|
||||
if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.');
|
||||
|
||||
return {
|
||||
embeds: [
|
||||
{
|
||||
embedId: upcloudScraper.id,
|
||||
url: upcloudSource.link,
|
||||
},
|
||||
],
|
||||
embeds: filteredEmbeds,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -1,31 +1,30 @@
|
||||
import { load } from 'cheerio';
|
||||
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
import { gomoviesBase } from '.';
|
||||
|
||||
export async function getSource(ctx: ScrapeContext, sources: any) {
|
||||
const upcloud = load(sources)('a[title*="upcloud" i]');
|
||||
export async function getSource(ctx: ScrapeContext, sources: any, title: string) {
|
||||
const source = load(sources)(`a[title*=${title} i]`);
|
||||
|
||||
const upcloudDataId = upcloud?.attr('data-id') ?? upcloud?.attr('data-linkid');
|
||||
const sourceDataId = source?.attr('data-id') ?? source?.attr('data-linkid');
|
||||
|
||||
if (!upcloudDataId) throw new NotFoundError('Upcloud source not available');
|
||||
if (!sourceDataId) return undefined;
|
||||
|
||||
const upcloudSource = await ctx.proxiedFetcher<{
|
||||
const sourceData = await ctx.proxiedFetcher<{
|
||||
type: 'iframe' | string;
|
||||
link: string;
|
||||
sources: [];
|
||||
title: string;
|
||||
tracks: [];
|
||||
}>(`/ajax/sources/${upcloudDataId}`, {
|
||||
}>(`/ajax/sources/${sourceDataId}`, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
baseUrl: gomoviesBase,
|
||||
});
|
||||
|
||||
if (!upcloudSource.link || upcloudSource.type !== 'iframe') throw new NotFoundError('No upcloud stream found');
|
||||
if (!sourceData.link || sourceData.type !== 'iframe') return undefined;
|
||||
|
||||
return upcloudSource;
|
||||
return sourceData;
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ async function universalScraper(ctx: ShowScrapeContext | MovieScrapeContext): Pr
|
||||
export const goojaraScraper = makeSourcerer({
|
||||
id: 'goojara',
|
||||
name: 'Goojara',
|
||||
rank: 225,
|
||||
rank: 70,
|
||||
flags: [],
|
||||
disabled: true,
|
||||
scrapeShow: universalScraper,
|
||||
|
@@ -120,7 +120,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
|
||||
export const hdRezkaScraper = makeSourcerer({
|
||||
id: 'hdrezka',
|
||||
name: 'HDRezka',
|
||||
rank: 195,
|
||||
rank: 120,
|
||||
flags: [flags.CORS_ALLOWED, flags.IP_LOCKED],
|
||||
scrapeShow: universalScraper,
|
||||
scrapeMovie: universalScraper,
|
||||
|
@@ -12,7 +12,7 @@ import { search } from './search';
|
||||
export const kissAsianScraper = makeSourcerer({
|
||||
id: 'kissasian',
|
||||
name: 'KissAsian',
|
||||
rank: 130,
|
||||
rank: 40,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
disabled: true,
|
||||
|
||||
|
@@ -33,7 +33,7 @@ export const lookmovieScraper = makeSourcerer({
|
||||
id: 'lookmovie',
|
||||
name: 'LookMovie',
|
||||
disabled: true,
|
||||
rank: 700,
|
||||
rank: 50,
|
||||
flags: [flags.IP_LOCKED],
|
||||
scrapeShow: universalScraper,
|
||||
scrapeMovie: universalScraper,
|
||||
|
@@ -76,7 +76,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) =>
|
||||
export const nepuScraper = makeSourcerer({
|
||||
id: 'nepu',
|
||||
name: 'Nepu',
|
||||
rank: 111,
|
||||
rank: 80,
|
||||
flags: [],
|
||||
disabled: true,
|
||||
scrapeMovie: universalScraper,
|
||||
|
2
src/providers/sources/primewire/common.ts
Normal file
2
src/providers/sources/primewire/common.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const primewireBase = 'https://www.primewire.tf';
|
||||
export const primewireApiKey = atob('bHpRUHNYU0tjRw==');
|
7
src/providers/sources/primewire/decryption/README.md
Normal file
7
src/providers/sources/primewire/decryption/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Maintaining decryption
|
||||
|
||||
This folder contains the decryption logic for the primewire provider
|
||||
|
||||
The code in `blowfish.ts` is a de-obfuscated version of the original code that is used to decrypt the video links. You can find original the code [in this JavaScript file](https://www.primewire.tf/js/app-21205005105979fb964d17bf03570023.js?vsn=d]) by searching for the keyword "sBox0".
|
||||
|
||||
The code is minified, so use prettier to deobfuscate it. In the case that the URL changes, you can find it used in the [primewire homepage](https://www.primewire.tf/).
|
263
src/providers/sources/primewire/decryption/blowfish.ts
Normal file
263
src/providers/sources/primewire/decryption/blowfish.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
import { pArray, sBox0, sBox1, sBox2, sBox3 } from './constants';
|
||||
|
||||
class Blowfish {
|
||||
sBox0: number[];
|
||||
|
||||
sBox1: number[];
|
||||
|
||||
sBox2: number[];
|
||||
|
||||
sBox3: number[];
|
||||
|
||||
pArray: number[];
|
||||
|
||||
keyStr: string;
|
||||
|
||||
iv: string;
|
||||
|
||||
constructor(t: string) {
|
||||
this.sBox0 = sBox0.slice();
|
||||
this.sBox1 = sBox1.slice();
|
||||
this.sBox2 = sBox2.slice();
|
||||
this.sBox3 = sBox3.slice();
|
||||
this.pArray = pArray.slice();
|
||||
this.keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
this.iv = 'abc12345';
|
||||
this.generateSubkeys(t);
|
||||
}
|
||||
|
||||
encrypt(e: string) {
|
||||
const root = this.utf8Decode(e);
|
||||
let encrypted = '';
|
||||
const blockSize = 8;
|
||||
const paddingChar = '\0';
|
||||
const numBlocks = Math.ceil(e.length / blockSize);
|
||||
|
||||
for (let i = 0; i < numBlocks; i++) {
|
||||
let block = root.substr(blockSize * i, blockSize);
|
||||
|
||||
if (block.length < blockSize) {
|
||||
block += paddingChar.repeat(blockSize - block.length);
|
||||
}
|
||||
|
||||
let [left, right] = this.split64by32(block);
|
||||
[left, right] = this.encipher(left, right);
|
||||
|
||||
encrypted += this.num2block32(left) + this.num2block32(right);
|
||||
}
|
||||
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
decrypt(input: string) {
|
||||
const numBlocks = Math.ceil(input.length / 8);
|
||||
let decrypted = '';
|
||||
for (let i = 0; i < numBlocks; i++) {
|
||||
const block = input.substr(8 * i, 8);
|
||||
if (block.length < 8) {
|
||||
throw new Error('Invalid block size');
|
||||
}
|
||||
const [left, right] = this.split64by32(block);
|
||||
const [decipheredLeft, decipheredRight] = this.decipher(left, right);
|
||||
decrypted += this.num2block32(decipheredLeft) + this.num2block32(decipheredRight);
|
||||
}
|
||||
return this.utf8Encode(decrypted);
|
||||
}
|
||||
|
||||
substitute(value: number) {
|
||||
const t = value >>> 24;
|
||||
const n = (value << 8) >>> 24;
|
||||
const r = (value << 16) >>> 24;
|
||||
const i = (value << 24) >>> 24;
|
||||
let result = this.addMod32(this.sBox0[t], this.sBox1[n]);
|
||||
result = this.xor(result, this.sBox2[r]);
|
||||
result = this.addMod32(result, this.sBox3[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
encipher(plaintext: number, key: number) {
|
||||
for (var temp, round = 0; round < 16; round++) {
|
||||
temp = plaintext = this.xor(plaintext, this.pArray[round]);
|
||||
plaintext = key = this.xor(this.substitute(plaintext), key);
|
||||
key = temp;
|
||||
}
|
||||
temp = plaintext;
|
||||
plaintext = key;
|
||||
key = temp;
|
||||
key = this.xor(key, this.pArray[16]);
|
||||
|
||||
return [(plaintext = this.xor(plaintext, this.pArray[17])), key];
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
decipher(left: number, right: number) {
|
||||
let n;
|
||||
let e = left;
|
||||
let t = right;
|
||||
n = this.xor(e, this.pArray[17]);
|
||||
e = this.xor(t, this.pArray[16]);
|
||||
t = n;
|
||||
for (let r = 15; r >= 0; r--) {
|
||||
n = e;
|
||||
e = t;
|
||||
t = n;
|
||||
t = this.xor(this.substitute(e), t);
|
||||
e = this.xor(e, this.pArray[r]);
|
||||
}
|
||||
return [e, t];
|
||||
}
|
||||
|
||||
generateSubkeys(key: string) {
|
||||
let temp;
|
||||
let keyIndex = 0;
|
||||
let pIndex = 0;
|
||||
for (let i = 0; i < 18; i++) {
|
||||
temp = 0;
|
||||
for (let j = 0; j < 4; j++) {
|
||||
temp = this.fixNegative((temp << 8) | key.charCodeAt(keyIndex));
|
||||
keyIndex = (keyIndex + 1) % key.length;
|
||||
}
|
||||
this.pArray[pIndex] = this.xor(this.pArray[pIndex], temp);
|
||||
pIndex++;
|
||||
}
|
||||
let tempSubkey = [0, 0];
|
||||
for (let i = 0; i < 18; i += 2) {
|
||||
tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]);
|
||||
this.pArray[i] = tempSubkey[0];
|
||||
this.pArray[i + 1] = tempSubkey[1];
|
||||
}
|
||||
for (let i = 0; i < 256; i += 2) {
|
||||
tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]);
|
||||
this.sBox0[i] = tempSubkey[0];
|
||||
this.sBox0[i + 1] = tempSubkey[1];
|
||||
}
|
||||
for (let i = 0; i < 256; i += 2) {
|
||||
tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]);
|
||||
this.sBox1[i] = tempSubkey[0];
|
||||
this.sBox1[i + 1] = tempSubkey[1];
|
||||
}
|
||||
for (let i = 0; i < 256; i += 2) {
|
||||
tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]);
|
||||
this.sBox2[i] = tempSubkey[0];
|
||||
this.sBox2[i + 1] = tempSubkey[1];
|
||||
}
|
||||
for (let i = 0; i < 256; i += 2) {
|
||||
tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]);
|
||||
this.sBox3[i] = tempSubkey[0];
|
||||
this.sBox3[i + 1] = tempSubkey[1];
|
||||
}
|
||||
}
|
||||
|
||||
block32toNum(e: string) {
|
||||
return this.fixNegative(
|
||||
(e.charCodeAt(0) << 24) | (e.charCodeAt(1) << 16) | (e.charCodeAt(2) << 8) | e.charCodeAt(3),
|
||||
);
|
||||
}
|
||||
|
||||
num2block32(e: number) {
|
||||
return (
|
||||
String.fromCharCode(e >>> 24) +
|
||||
String.fromCharCode((e << 8) >>> 24) +
|
||||
String.fromCharCode((e << 16) >>> 24) +
|
||||
String.fromCharCode((e << 24) >>> 24)
|
||||
);
|
||||
}
|
||||
|
||||
xor(e: number, t: number) {
|
||||
return this.fixNegative(e ^ t);
|
||||
}
|
||||
|
||||
addMod32(e: number, t: number) {
|
||||
return this.fixNegative((e + t) | 0);
|
||||
}
|
||||
|
||||
fixNegative(e: number) {
|
||||
return e >>> 0;
|
||||
}
|
||||
|
||||
split64by32(e: string) {
|
||||
const t = e.substring(0, 4);
|
||||
const n = e.substring(4, 8);
|
||||
return [this.block32toNum(t), this.block32toNum(n)];
|
||||
}
|
||||
|
||||
utf8Decode(input: string) {
|
||||
let decoded = '';
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const charCode = input.charCodeAt(i);
|
||||
if (charCode < 128) {
|
||||
decoded += String.fromCharCode(charCode);
|
||||
} else if (charCode > 127 && charCode < 2048) {
|
||||
const firstCharCode = (charCode >> 6) | 192;
|
||||
const secondCharCode = (63 & charCode) | 128;
|
||||
decoded += String.fromCharCode(firstCharCode, secondCharCode);
|
||||
} else {
|
||||
const firstCharCode = (charCode >> 12) | 224;
|
||||
const secondCharCode = ((charCode >> 6) & 63) | 128;
|
||||
const thirdCharCode = (63 & charCode) | 128;
|
||||
decoded += String.fromCharCode(firstCharCode, secondCharCode, thirdCharCode);
|
||||
}
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
utf8Encode(input: string) {
|
||||
let encoded = '';
|
||||
let charCode;
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
charCode = input.charCodeAt(i);
|
||||
if (charCode < 128) {
|
||||
encoded += String.fromCharCode(charCode);
|
||||
} else if (charCode > 191 && charCode < 224) {
|
||||
const secondCharCode = input.charCodeAt(i + 1);
|
||||
encoded += String.fromCharCode(((31 & charCode) << 6) | (63 & secondCharCode));
|
||||
i += 1;
|
||||
} else {
|
||||
const secondCharCode = input.charCodeAt(i + 1);
|
||||
const thirdCharCode = input.charCodeAt(i + 2);
|
||||
encoded += String.fromCharCode(((15 & charCode) << 12) | ((63 & secondCharCode) << 6) | (63 & thirdCharCode));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
base64(e: string) {
|
||||
let t;
|
||||
let n;
|
||||
let r;
|
||||
let i;
|
||||
let o;
|
||||
let a;
|
||||
let s = '';
|
||||
let l = 0;
|
||||
const root = e.replace(/[^A-Za-z0-9\\+\\/=]/g, '');
|
||||
while (l < root.length) {
|
||||
t = (this.keyStr.indexOf(root.charAt(l++)) << 2) | ((i = this.keyStr.indexOf(root.charAt(l++))) >> 4);
|
||||
n = ((15 & i) << 4) | ((o = this.keyStr.indexOf(root.charAt(l++))) >> 2);
|
||||
r = ((3 & o) << 6) | (a = this.keyStr.indexOf(root.charAt(l++)));
|
||||
s += String.fromCharCode(t);
|
||||
if (o !== 64) {
|
||||
s += String.fromCharCode(n);
|
||||
}
|
||||
if (a !== 64) {
|
||||
s += String.fromCharCode(r);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLinks(encryptedInput: string) {
|
||||
const key = encryptedInput.slice(-10);
|
||||
const data = encryptedInput.slice(0, -10);
|
||||
const cipher = new Blowfish(key);
|
||||
const decryptedData = cipher.decrypt(cipher.base64(data)).match(/.{1,5}/g);
|
||||
|
||||
if (!decryptedData) {
|
||||
throw new Error('No links found');
|
||||
} else {
|
||||
return decryptedData;
|
||||
}
|
||||
}
|
116
src/providers/sources/primewire/decryption/constants.ts
Normal file
116
src/providers/sources/primewire/decryption/constants.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
export const pArray = [
|
||||
608135816, 2242054355, 320440878, 57701188, 2752067618, 698298832, 137296536, 3964562569, 1160258022, 953160567,
|
||||
3193202383, 887688300, 3232508343, 3380367581, 1065670069, 3041331479, 2450970073, 2306472731,
|
||||
];
|
||||
export const sBox0 = [
|
||||
3509652390, 2564797868, 805139163, 3491422135, 3101798381, 1780907670, 3128725573, 4046225305, 614570311, 3012652279,
|
||||
134345442, 2240740374, 1667834072, 1901547113, 2757295779, 4103290238, 227898511, 1921955416, 1904987480, 2182433518,
|
||||
2069144605, 3260701109, 2620446009, 720527379, 3318853667, 677414384, 3393288472, 3101374703, 2390351024, 1614419982,
|
||||
1822297739, 2954791486, 3608508353, 3174124327, 2024746970, 1432378464, 3864339955, 2857741204, 1464375394,
|
||||
1676153920, 1439316330, 715854006, 3033291828, 289532110, 2706671279, 2087905683, 3018724369, 1668267050, 732546397,
|
||||
1947742710, 3462151702, 2609353502, 2950085171, 1814351708, 2050118529, 680887927, 999245976, 1800124847, 3300911131,
|
||||
1713906067, 1641548236, 4213287313, 1216130144, 1575780402, 4018429277, 3917837745, 3693486850, 3949271944, 596196993,
|
||||
3549867205, 258830323, 2213823033, 772490370, 2760122372, 1774776394, 2652871518, 566650946, 4142492826, 1728879713,
|
||||
2882767088, 1783734482, 3629395816, 2517608232, 2874225571, 1861159788, 326777828, 3124490320, 2130389656, 2716951837,
|
||||
967770486, 1724537150, 2185432712, 2364442137, 1164943284, 2105845187, 998989502, 3765401048, 2244026483, 1075463327,
|
||||
1455516326, 1322494562, 910128902, 469688178, 1117454909, 936433444, 3490320968, 3675253459, 1240580251, 122909385,
|
||||
2157517691, 634681816, 4142456567, 3825094682, 3061402683, 2540495037, 79693498, 3249098678, 1084186820, 1583128258,
|
||||
426386531, 1761308591, 1047286709, 322548459, 995290223, 1845252383, 2603652396, 3431023940, 2942221577, 3202600964,
|
||||
3727903485, 1712269319, 422464435, 3234572375, 1170764815, 3523960633, 3117677531, 1434042557, 442511882, 3600875718,
|
||||
1076654713, 1738483198, 4213154764, 2393238008, 3677496056, 1014306527, 4251020053, 793779912, 2902807211, 842905082,
|
||||
4246964064, 1395751752, 1040244610, 2656851899, 3396308128, 445077038, 3742853595, 3577915638, 679411651, 2892444358,
|
||||
2354009459, 1767581616, 3150600392, 3791627101, 3102740896, 284835224, 4246832056, 1258075500, 768725851, 2589189241,
|
||||
3069724005, 3532540348, 1274779536, 3789419226, 2764799539, 1660621633, 3471099624, 4011903706, 913787905, 3497959166,
|
||||
737222580, 2514213453, 2928710040, 3937242737, 1804850592, 3499020752, 2949064160, 2386320175, 2390070455, 2415321851,
|
||||
4061277028, 2290661394, 2416832540, 1336762016, 1754252060, 3520065937, 3014181293, 791618072, 3188594551, 3933548030,
|
||||
2332172193, 3852520463, 3043980520, 413987798, 3465142937, 3030929376, 4245938359, 2093235073, 3534596313, 375366246,
|
||||
2157278981, 2479649556, 555357303, 3870105701, 2008414854, 3344188149, 4221384143, 3956125452, 2067696032, 3594591187,
|
||||
2921233993, 2428461, 544322398, 577241275, 1471733935, 610547355, 4027169054, 1432588573, 1507829418, 2025931657,
|
||||
3646575487, 545086370, 48609733, 2200306550, 1653985193, 298326376, 1316178497, 3007786442, 2064951626, 458293330,
|
||||
2589141269, 3591329599, 3164325604, 727753846, 2179363840, 146436021, 1461446943, 4069977195, 705550613, 3059967265,
|
||||
3887724982, 4281599278, 3313849956, 1404054877, 2845806497, 146425753, 1854211946,
|
||||
];
|
||||
export const sBox1 = [
|
||||
1266315497, 3048417604, 3681880366, 3289982499, 290971e4, 1235738493, 2632868024, 2414719590, 3970600049, 1771706367,
|
||||
1449415276, 3266420449, 422970021, 1963543593, 2690192192, 3826793022, 1062508698, 1531092325, 1804592342, 2583117782,
|
||||
2714934279, 4024971509, 1294809318, 4028980673, 1289560198, 2221992742, 1669523910, 35572830, 157838143, 1052438473,
|
||||
1016535060, 1802137761, 1753167236, 1386275462, 3080475397, 2857371447, 1040679964, 2145300060, 2390574316,
|
||||
1461121720, 2956646967, 4031777805, 4028374788, 33600511, 2920084762, 1018524850, 629373528, 3691585981, 3515945977,
|
||||
2091462646, 2486323059, 586499841, 988145025, 935516892, 3367335476, 2599673255, 2839830854, 265290510, 3972581182,
|
||||
2759138881, 3795373465, 1005194799, 847297441, 406762289, 1314163512, 1332590856, 1866599683, 4127851711, 750260880,
|
||||
613907577, 1450815602, 3165620655, 3734664991, 3650291728, 3012275730, 3704569646, 1427272223, 778793252, 1343938022,
|
||||
2676280711, 2052605720, 1946737175, 3164576444, 3914038668, 3967478842, 3682934266, 1661551462, 3294938066,
|
||||
4011595847, 840292616, 3712170807, 616741398, 312560963, 711312465, 1351876610, 322626781, 1910503582, 271666773,
|
||||
2175563734, 1594956187, 70604529, 3617834859, 1007753275, 1495573769, 4069517037, 2549218298, 2663038764, 504708206,
|
||||
2263041392, 3941167025, 2249088522, 1514023603, 1998579484, 1312622330, 694541497, 2582060303, 2151582166, 1382467621,
|
||||
776784248, 2618340202, 3323268794, 2497899128, 2784771155, 503983604, 4076293799, 907881277, 423175695, 432175456,
|
||||
1378068232, 4145222326, 3954048622, 3938656102, 3820766613, 2793130115, 2977904593, 26017576, 3274890735, 3194772133,
|
||||
1700274565, 1756076034, 4006520079, 3677328699, 720338349, 1533947780, 354530856, 688349552, 3973924725, 1637815568,
|
||||
332179504, 3949051286, 53804574, 2852348879, 3044236432, 1282449977, 3583942155, 3416972820, 4006381244, 1617046695,
|
||||
2628476075, 3002303598, 1686838959, 431878346, 2686675385, 1700445008, 1080580658, 1009431731, 832498133, 3223435511,
|
||||
2605976345, 2271191193, 2516031870, 1648197032, 4164389018, 2548247927, 300782431, 375919233, 238389289, 3353747414,
|
||||
2531188641, 2019080857, 1475708069, 455242339, 2609103871, 448939670, 3451063019, 1395535956, 2413381860, 1841049896,
|
||||
1491858159, 885456874, 4264095073, 4001119347, 1565136089, 3898914787, 1108368660, 540939232, 1173283510, 2745871338,
|
||||
3681308437, 4207628240, 3343053890, 4016749493, 1699691293, 1103962373, 3625875870, 2256883143, 3830138730,
|
||||
1031889488, 3479347698, 1535977030, 4236805024, 3251091107, 2132092099, 1774941330, 1199868427, 1452454533, 157007616,
|
||||
2904115357, 342012276, 595725824, 1480756522, 206960106, 497939518, 591360097, 863170706, 2375253569, 3596610801,
|
||||
1814182875, 2094937945, 3421402208, 1082520231, 3463918190, 2785509508, 435703966, 3908032597, 1641649973, 2842273706,
|
||||
3305899714, 1510255612, 2148256476, 2655287854, 3276092548, 4258621189, 236887753, 3681803219, 274041037, 1734335097,
|
||||
3815195456, 3317970021, 1899903192, 1026095262, 4050517792, 356393447, 2410691914, 3873677099, 3682840055,
|
||||
];
|
||||
export const sBox2 = [
|
||||
3913112168, 2491498743, 4132185628, 2489919796, 1091903735, 1979897079, 3170134830, 3567386728, 3557303409, 857797738,
|
||||
1136121015, 1342202287, 507115054, 2535736646, 337727348, 3213592640, 1301675037, 2528481711, 1895095763, 1721773893,
|
||||
3216771564, 62756741, 2142006736, 835421444, 2531993523, 1442658625, 3659876326, 2882144922, 676362277, 1392781812,
|
||||
170690266, 3921047035, 1759253602, 3611846912, 1745797284, 664899054, 1329594018, 3901205900, 3045908486, 2062866102,
|
||||
2865634940, 3543621612, 3464012697, 1080764994, 553557557, 3656615353, 3996768171, 991055499, 499776247, 1265440854,
|
||||
648242737, 3940784050, 980351604, 3713745714, 1749149687, 3396870395, 4211799374, 3640570775, 1161844396, 3125318951,
|
||||
1431517754, 545492359, 4268468663, 3499529547, 1437099964, 2702547544, 3433638243, 2581715763, 2787789398, 1060185593,
|
||||
1593081372, 2418618748, 4260947970, 69676912, 2159744348, 86519011, 2512459080, 3838209314, 1220612927, 3339683548,
|
||||
133810670, 1090789135, 1078426020, 1569222167, 845107691, 3583754449, 4072456591, 1091646820, 628848692, 1613405280,
|
||||
3757631651, 526609435, 236106946, 48312990, 2942717905, 3402727701, 1797494240, 859738849, 992217954, 4005476642,
|
||||
2243076622, 3870952857, 3732016268, 765654824, 3490871365, 2511836413, 1685915746, 3888969200, 1414112111, 2273134842,
|
||||
3281911079, 4080962846, 172450625, 2569994100, 980381355, 4109958455, 2819808352, 2716589560, 2568741196, 3681446669,
|
||||
3329971472, 1835478071, 660984891, 3704678404, 4045999559, 3422617507, 3040415634, 1762651403, 1719377915, 3470491036,
|
||||
2693910283, 3642056355, 3138596744, 1364962596, 2073328063, 1983633131, 926494387, 3423689081, 2150032023, 4096667949,
|
||||
1749200295, 3328846651, 309677260, 2016342300, 1779581495, 3079819751, 111262694, 1274766160, 443224088, 298511866,
|
||||
1025883608, 3806446537, 1145181785, 168956806, 3641502830, 3584813610, 1689216846, 3666258015, 3200248200, 1692713982,
|
||||
2646376535, 4042768518, 1618508792, 1610833997, 3523052358, 4130873264, 2001055236, 3610705100, 2202168115,
|
||||
4028541809, 2961195399, 1006657119, 2006996926, 3186142756, 1430667929, 3210227297, 1314452623, 4074634658,
|
||||
4101304120, 2273951170, 1399257539, 3367210612, 3027628629, 1190975929, 2062231137, 2333990788, 2221543033,
|
||||
2438960610, 1181637006, 548689776, 2362791313, 3372408396, 3104550113, 3145860560, 296247880, 1970579870, 3078560182,
|
||||
3769228297, 1714227617, 3291629107, 3898220290, 166772364, 1251581989, 493813264, 448347421, 195405023, 2709975567,
|
||||
677966185, 3703036547, 1463355134, 2715995803, 1338867538, 1343315457, 2802222074, 2684532164, 233230375, 2599980071,
|
||||
2000651841, 3277868038, 1638401717, 4028070440, 3237316320, 6314154, 819756386, 300326615, 590932579, 1405279636,
|
||||
3267499572, 3150704214, 2428286686, 3959192993, 3461946742, 1862657033, 1266418056, 963775037, 2089974820, 2263052895,
|
||||
1917689273, 448879540, 3550394620, 3981727096, 150775221, 3627908307, 1303187396, 508620638, 2975983352, 2726630617,
|
||||
1817252668, 1876281319, 1457606340, 908771278, 3720792119, 3617206836, 2455994898, 1729034894, 1080033504,
|
||||
];
|
||||
export const sBox3 = [
|
||||
976866871, 3556439503, 2881648439, 1522871579, 1555064734, 1336096578, 3548522304, 2579274686, 3574697629, 3205460757,
|
||||
3593280638, 3338716283, 3079412587, 564236357, 2993598910, 1781952180, 1464380207, 3163844217, 3332601554, 1699332808,
|
||||
1393555694, 1183702653, 3581086237, 1288719814, 691649499, 2847557200, 2895455976, 3193889540, 2717570544, 1781354906,
|
||||
1676643554, 2592534050, 3230253752, 1126444790, 2770207658, 2633158820, 2210423226, 2615765581, 2414155088,
|
||||
3127139286, 673620729, 2805611233, 1269405062, 4015350505, 3341807571, 4149409754, 1057255273, 2012875353, 2162469141,
|
||||
2276492801, 2601117357, 993977747, 3918593370, 2654263191, 753973209, 36408145, 2530585658, 25011837, 3520020182,
|
||||
2088578344, 530523599, 2918365339, 1524020338, 1518925132, 3760827505, 3759777254, 1202760957, 3985898139, 3906192525,
|
||||
674977740, 4174734889, 2031300136, 2019492241, 3983892565, 4153806404, 3822280332, 352677332, 2297720250, 60907813,
|
||||
90501309, 3286998549, 1016092578, 2535922412, 2839152426, 457141659, 509813237, 4120667899, 652014361, 1966332200,
|
||||
2975202805, 55981186, 2327461051, 676427537, 3255491064, 2882294119, 3433927263, 1307055953, 942726286, 933058658,
|
||||
2468411793, 3933900994, 4215176142, 1361170020, 2001714738, 2830558078, 3274259782, 1222529897, 1679025792,
|
||||
2729314320, 3714953764, 1770335741, 151462246, 3013232138, 1682292957, 1483529935, 471910574, 1539241949, 458788160,
|
||||
3436315007, 1807016891, 3718408830, 978976581, 1043663428, 3165965781, 1927990952, 4200891579, 2372276910, 3208408903,
|
||||
3533431907, 1412390302, 2931980059, 4132332400, 1947078029, 3881505623, 4168226417, 2941484381, 1077988104,
|
||||
1320477388, 886195818, 18198404, 3786409e3, 2509781533, 112762804, 3463356488, 1866414978, 891333506, 18488651,
|
||||
661792760, 1628790961, 3885187036, 3141171499, 876946877, 2693282273, 1372485963, 791857591, 2686433993, 3759982718,
|
||||
3167212022, 3472953795, 2716379847, 445679433, 3561995674, 3504004811, 3574258232, 54117162, 3331405415, 2381918588,
|
||||
3769707343, 4154350007, 1140177722, 4074052095, 668550556, 3214352940, 367459370, 261225585, 2610173221, 4209349473,
|
||||
3468074219, 3265815641, 314222801, 3066103646, 3808782860, 282218597, 3406013506, 3773591054, 379116347, 1285071038,
|
||||
846784868, 2669647154, 3771962079, 3550491691, 2305946142, 453669953, 1268987020, 3317592352, 3279303384, 3744833421,
|
||||
2610507566, 3859509063, 266596637, 3847019092, 517658769, 3462560207, 3443424879, 370717030, 4247526661, 2224018117,
|
||||
4143653529, 4112773975, 2788324899, 2477274417, 1456262402, 2901442914, 1517677493, 1846949527, 2295493580,
|
||||
3734397586, 2176403920, 1280348187, 1908823572, 3871786941, 846861322, 1172426758, 3287448474, 3383383037, 1655181056,
|
||||
3139813346, 901632758, 1897031941, 2986607138, 3066810236, 3447102507, 1393639104, 373351379, 950779232, 625454576,
|
||||
3124240540, 4148612726, 2007998917, 544563296, 2244738638, 2330496472, 2058025392, 1291430526, 424198748, 50039436,
|
||||
29584100, 3605783033, 2429876329, 2791104160, 1057563949, 3255363231, 3075367218, 3463963227, 1469046755, 985887462,
|
||||
];
|
118
src/providers/sources/primewire/index.ts
Normal file
118
src/providers/sources/primewire/index.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { load } from 'cheerio';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { makeSourcerer } from '@/providers/base';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
import { primewireApiKey, primewireBase } from './common';
|
||||
import { getLinks } from './decryption/blowfish';
|
||||
|
||||
async function search(ctx: ScrapeContext, imdbId: string) {
|
||||
const searchResult = await ctx.proxiedFetcher<{
|
||||
id: string;
|
||||
}>('/api/v1/show/', {
|
||||
baseUrl: primewireBase,
|
||||
query: {
|
||||
key: primewireApiKey,
|
||||
imdb_id: imdbId,
|
||||
},
|
||||
});
|
||||
|
||||
return searchResult.id;
|
||||
}
|
||||
|
||||
async function getStreams(title: string) {
|
||||
const titlePage = load(title);
|
||||
const userData = titlePage('#user-data').attr('v');
|
||||
if (!userData) throw new NotFoundError('No user data found');
|
||||
|
||||
const links = getLinks(userData);
|
||||
|
||||
const embeds = [];
|
||||
|
||||
if (!links) throw new NotFoundError('No links found');
|
||||
|
||||
for (const link in links) {
|
||||
if (link.includes(link)) {
|
||||
const element = titlePage(`.propper-link[link_version='${link}']`);
|
||||
const sourceName = element.parent().parent().parent().find('.version-host').text().trim();
|
||||
let embedId;
|
||||
switch (sourceName) {
|
||||
case 'mixdrop.co':
|
||||
embedId = 'mixdrop';
|
||||
break;
|
||||
case 'voe.sx':
|
||||
embedId = 'voe';
|
||||
break;
|
||||
case 'upstream.to':
|
||||
embedId = 'upstream';
|
||||
break;
|
||||
case 'streamvid.net':
|
||||
embedId = 'streamvid';
|
||||
break;
|
||||
case 'dood.watch':
|
||||
embedId = 'dood';
|
||||
break;
|
||||
default:
|
||||
embedId = null;
|
||||
}
|
||||
if (!embedId) continue;
|
||||
embeds.push({
|
||||
url: `${primewireBase}/links/go/${links[link]}`,
|
||||
embedId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return embeds;
|
||||
}
|
||||
|
||||
export const primewireScraper = makeSourcerer({
|
||||
id: 'primewire',
|
||||
name: 'Primewire',
|
||||
rank: 110,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
async scrapeMovie(ctx) {
|
||||
if (!ctx.media.imdbId) throw new Error('No imdbId provided');
|
||||
const searchResult = await search(ctx, ctx.media.imdbId);
|
||||
|
||||
const title = await ctx.proxiedFetcher<string>(`movie/${searchResult}`, {
|
||||
baseUrl: primewireBase,
|
||||
});
|
||||
|
||||
const embeds = await getStreams(title);
|
||||
|
||||
return {
|
||||
embeds,
|
||||
};
|
||||
},
|
||||
async scrapeShow(ctx) {
|
||||
if (!ctx.media.imdbId) throw new Error('No imdbId provided');
|
||||
const searchResult = await search(ctx, ctx.media.imdbId);
|
||||
|
||||
const season = await ctx.proxiedFetcher<string>(`tv/${searchResult}`, {
|
||||
baseUrl: primewireBase,
|
||||
});
|
||||
|
||||
const seasonPage = load(season);
|
||||
|
||||
const episodeLink = seasonPage(`.show_season[data-id='${ctx.media.season.number}'] > div > a`)
|
||||
.toArray()
|
||||
.find((link) => {
|
||||
return link.attribs.href.includes(`-episode-${ctx.media.episode.number}`);
|
||||
})?.attribs.href;
|
||||
|
||||
if (!episodeLink) throw new NotFoundError('No episode links found');
|
||||
|
||||
const title = await ctx.proxiedFetcher<string>(episodeLink, {
|
||||
baseUrl: primewireBase,
|
||||
});
|
||||
|
||||
const embeds = await getStreams(title);
|
||||
|
||||
return {
|
||||
embeds,
|
||||
};
|
||||
},
|
||||
});
|
@@ -11,7 +11,7 @@ export const remotestreamScraper = makeSourcerer({
|
||||
id: 'remotestream',
|
||||
name: 'Remote Stream',
|
||||
disabled: true,
|
||||
rank: 55,
|
||||
rank: 20,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
async scrapeShow(ctx) {
|
||||
const seasonNumber = ctx.media.season.number;
|
||||
|
@@ -68,7 +68,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) =>
|
||||
export const ridooMoviesScraper = makeSourcerer({
|
||||
id: 'ridomovies',
|
||||
name: 'RidoMovies',
|
||||
rank: 105,
|
||||
rank: 100,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: universalScraper,
|
||||
scrapeShow: universalScraper,
|
||||
|
@@ -41,7 +41,7 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis
|
||||
export const showboxScraper = makeSourcerer({
|
||||
id: 'showbox',
|
||||
name: 'Showbox',
|
||||
rank: 400,
|
||||
rank: 150,
|
||||
flags: [flags.CORS_ALLOWED, flags.CF_BLOCKED],
|
||||
scrapeShow: comboScraper,
|
||||
scrapeMovie: comboScraper,
|
||||
|
@@ -57,7 +57,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr
|
||||
export const smashyStreamScraper = makeSourcerer({
|
||||
id: 'smashystream',
|
||||
name: 'SmashyStream',
|
||||
rank: 70,
|
||||
rank: 30,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
disabled: true,
|
||||
scrapeMovie: universalScraper,
|
||||
|
@@ -6,7 +6,7 @@ import { scrapeShow } from '@/providers/sources/vidsrc/scrape-show';
|
||||
export const vidsrcScraper = makeSourcerer({
|
||||
id: 'vidsrc',
|
||||
name: 'VidSrc',
|
||||
rank: 120,
|
||||
rank: 90,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie,
|
||||
scrapeShow,
|
||||
|
@@ -79,5 +79,5 @@ export const vidSrcToScraper = makeSourcerer({
|
||||
scrapeMovie: universalScraper,
|
||||
scrapeShow: universalScraper,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
rank: 300,
|
||||
rank: 130,
|
||||
});
|
||||
|
@@ -6,7 +6,7 @@ import { scrapeShow } from '@/providers/sources/zoechip/scrape-show';
|
||||
export const zoechipScraper = makeSourcerer({
|
||||
id: 'zoechip',
|
||||
name: 'ZoeChip',
|
||||
rank: 200,
|
||||
rank: 62,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
disabled: true,
|
||||
scrapeMovie,
|
||||
|
@@ -6,7 +6,7 @@ import { EmbedOutput, SourcererOutput } from '@/providers/base';
|
||||
import { ProviderList } from '@/providers/get';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { isValidStream } from '@/utils/valid';
|
||||
import { isValidStream, validatePlayableStreams } from '@/utils/valid';
|
||||
|
||||
export type IndividualSourceRunnerOptions = {
|
||||
features: FeatureMap;
|
||||
@@ -68,6 +68,13 @@ export async function scrapeInvidualSource(
|
||||
|
||||
if ((!output.stream || output.stream.length === 0) && output.embeds.length === 0)
|
||||
throw new NotFoundError('No streams found');
|
||||
|
||||
// only check for playable streams if there are streams, and if there are no embeds
|
||||
if (output.stream && output.stream.length > 0 && output.embeds.length === 0) {
|
||||
const playableStreams = await validatePlayableStreams(output.stream, ops);
|
||||
if (playableStreams.length === 0) throw new NotFoundError('No playable streams found');
|
||||
output.stream = playableStreams;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -105,5 +112,9 @@ export async function scrapeIndividualEmbed(
|
||||
.filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags));
|
||||
if (output.stream.length === 0) throw new NotFoundError('No streams found');
|
||||
|
||||
const playableStreams = await validatePlayableStreams(output.stream, ops);
|
||||
if (playableStreams.length === 0) throw new NotFoundError('No playable streams found');
|
||||
output.stream = playableStreams;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import { Stream } from '@/providers/streams';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { reorderOnIdList } from '@/utils/list';
|
||||
import { isValidStream } from '@/utils/valid';
|
||||
import { isValidStream, validatePlayableStream } from '@/utils/valid';
|
||||
|
||||
export type RunOutput = {
|
||||
sourceId: string;
|
||||
@@ -104,9 +104,11 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt
|
||||
|
||||
// return stream is there are any
|
||||
if (output.stream?.[0]) {
|
||||
const playableStream = await validatePlayableStream(output.stream[0], ops);
|
||||
if (!playableStream) throw new NotFoundError('No streams found');
|
||||
return {
|
||||
sourceId: source.id,
|
||||
stream: output.stream[0],
|
||||
stream: playableStream,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,6 +151,9 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt
|
||||
if (embedOutput.stream.length === 0) {
|
||||
throw new NotFoundError('No streams found');
|
||||
}
|
||||
const playableStream = await validatePlayableStream(embedOutput.stream[0], ops);
|
||||
if (!playableStream) throw new NotFoundError('No streams found');
|
||||
embedOutput.stream = [playableStream];
|
||||
} catch (error) {
|
||||
const updateParams: UpdateEvent = {
|
||||
id: source.id,
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import { Stream } from '@/providers/streams';
|
||||
import { IndividualEmbedRunnerOptions } from '@/runners/individualRunner';
|
||||
import { ProviderRunnerOptions } from '@/runners/runner';
|
||||
|
||||
export function isValidStream(stream: Stream | undefined): boolean {
|
||||
if (!stream) return false;
|
||||
@@ -15,3 +17,54 @@ export function isValidStream(stream: Stream | undefined): boolean {
|
||||
// unknown file type
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function validatePlayableStream(
|
||||
stream: Stream,
|
||||
ops: ProviderRunnerOptions | IndividualEmbedRunnerOptions,
|
||||
): Promise<Stream | null> {
|
||||
if (stream.type === 'hls') {
|
||||
const result = await ops.proxiedFetcher.full(stream.playlist, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...stream.preferredHeaders,
|
||||
...stream.headers,
|
||||
},
|
||||
});
|
||||
if (result.statusCode < 200 || result.statusCode >= 400) return null;
|
||||
return stream;
|
||||
}
|
||||
if (stream.type === 'file') {
|
||||
const validQualitiesResults = await Promise.all(
|
||||
Object.values(stream.qualities).map((quality) =>
|
||||
ops.proxiedFetcher.full(quality.url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...stream.preferredHeaders,
|
||||
...stream.headers,
|
||||
Range: 'bytes=0-1',
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
// remove invalid qualities from the stream
|
||||
const validQualities = stream.qualities;
|
||||
Object.keys(stream.qualities).forEach((quality, index) => {
|
||||
if (validQualitiesResults[index].statusCode < 200 || validQualitiesResults[index].statusCode >= 400) {
|
||||
delete validQualities[quality as keyof typeof stream.qualities];
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(validQualities).length === 0) return null;
|
||||
return { ...stream, qualities: validQualities };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function validatePlayableStreams(
|
||||
streams: Stream[],
|
||||
ops: ProviderRunnerOptions | IndividualEmbedRunnerOptions,
|
||||
): Promise<Stream[]> {
|
||||
return (await Promise.all(streams.map((stream) => validatePlayableStream(stream, ops)))).filter(
|
||||
(v) => v !== null,
|
||||
) as Stream[];
|
||||
}
|
||||
|
Reference in New Issue
Block a user