Merge branch 'dev' into vidcloud

This commit is contained in:
Jorrin 2024-01-28 00:45:36 +01:00
commit 03fc42fa9c
30 changed files with 590 additions and 65 deletions

View File

@ -2,6 +2,16 @@
title: 'Changelog' title: 'Changelog'
--- ---
# Version 2.1.1
- Fixed vidplay decryption keys being wrong and switched the domain to one that works
# Version 2.1.0
- Add preferedHeaders to most sources
- Add CF_BLOCKED flag to sources that have blocked cloudflare API's
- Fix vidsrc sometimes having an equal sign where it shouldnt
- Increase ranking of lookmovie
- Re-enabled subtitles for febbox-mp4
# Version 2.0.5 # Version 2.0.5
- Disable subtitles for febbox-mp4. As their endpoint doesn't work anymore. - Disable subtitles for febbox-mp4. As their endpoint doesn't work anymore.

View File

@ -17286,9 +17286,9 @@
"dev": true "dev": true
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "4.5.1", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
"integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==", "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",

42
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@movie-web/providers", "name": "@movie-web/providers",
"version": "2.0.5", "version": "2.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@movie-web/providers", "name": "@movie-web/providers",
"version": "2.0.5", "version": "2.1.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
@ -26,6 +26,7 @@
"@typescript-eslint/parser": "^5.60.0", "@typescript-eslint/parser": "^5.60.0",
"@vitest/coverage-v8": "^0.34.3", "@vitest/coverage-v8": "^0.34.3",
"commander": "^11.0.0", "commander": "^11.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"enquirer": "^2.4.1", "enquirer": "^2.4.1",
"eslint": "^8.30.0", "eslint": "^8.30.0",
@ -1794,6 +1795,24 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cross-env": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.1"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=10.14",
"npm": ">=6",
"yarn": ">=1"
}
},
"node_modules/cross-fetch": { "node_modules/cross-fetch": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@ -2848,6 +2867,20 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"dev": true, "dev": true,
@ -5517,9 +5550,10 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "4.5.1", "version": "4.5.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",
"postcss": "^8.4.27", "postcss": "^8.4.27",

View File

@ -1,6 +1,6 @@
{ {
"name": "@movie-web/providers", "name": "@movie-web/providers",
"version": "2.0.5", "version": "2.1.1",
"description": "Package that contains all the providers of movie-web", "description": "Package that contains all the providers of movie-web",
"main": "./lib/index.umd.js", "main": "./lib/index.umd.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
@ -38,6 +38,7 @@
"cli": "ts-node ./src/dev-cli/index.ts", "cli": "ts-node ./src/dev-cli/index.ts",
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"test:providers": "cross-env MW_TEST_PROVIDERS=true vitest run --reporter verbose",
"test:integration": "node ./tests/cjs && node ./tests/esm && node ./tests/browser", "test:integration": "node ./tests/cjs && node ./tests/esm && node ./tests/browser",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
"lint": "eslint --ext .ts,.js src/", "lint": "eslint --ext .ts,.js src/",
@ -55,6 +56,7 @@
"@typescript-eslint/parser": "^5.60.0", "@typescript-eslint/parser": "^5.60.0",
"@vitest/coverage-v8": "^0.34.3", "@vitest/coverage-v8": "^0.34.3",
"commander": "^11.0.0", "commander": "^11.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"enquirer": "^2.4.1", "enquirer": "^2.4.1",
"eslint": "^8.30.0", "eslint": "^8.30.0",

View File

@ -0,0 +1,90 @@
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;
source: Sourcerer;
testSuite: ScrapeMedia[];
types: TestTypes[];
debug?: boolean;
expect: {
embeds: number;
streams?: number;
error?: boolean;
}
}
function makeBaseEmbedProviders() {
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");
describe(`embed:${ops.source.id}:${ops.embed.id}`, () => {
ops.testSuite.forEach((test) => {
describe(`test ${test.title}`, async () => {
async function gatherEmbeds(providers: ProviderControls): Promise<SourcererEmbed[]> {
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})`);
return results.embeds;
}
async function runTest(providers: ProviderControls, embedUrl: string) {
let hasError = false;
let streamCount = 0;
try {
const result = await providers.runEmbedScraper({
id: ops.embed.id,
url: embedUrl,
})
if (ops.debug) console.log(result);
streamCount = (result.stream ?? []).length;
} catch (err) {
if (ops.debug) console.log(err);
hasError = true;
}
expect(ops.expect.error ?? false).toBe(hasError);
expect(ops.expect.streams ?? 0).toBe(streamCount);
}
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();
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");
builder.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch));
}
const providers = builder.build();
try {
const embeds = await gatherEmbeds(providers);
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);
})
}
}
})
})
})
}

View File

@ -0,0 +1,118 @@
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 { vidsrcembedScraper } from '@/providers/embeds/vidsrc';
import { vidsrcScraper } from '@/providers/sources/vidsrc';
import { vidSrcToScraper } from '@/providers/sources/vidsrcto';
import { vidplayScraper } from '@/providers/embeds/vidplay';
import { fileMoonScraper } from '@/providers/embeds/filemoon';
import { zoechipScraper } from '@/providers/sources/zoechip';
import { mixdropScraper } from '@/providers/embeds/mixdrop';
dotenv.config();
testEmbed({
embed: febboxMp4Scraper,
source: showboxScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: upcloudScraper,
source: flixhqScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: upcloudScraper,
source: goMoviesScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: smashyStreamDScraper,
source: smashyStreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: vidsrcembedScraper,
source: vidsrcScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: vidplayScraper,
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: fileMoonScraper,
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
streams: 1,
}
})
testEmbed({
embed: upcloudScraper,
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
streams: 1,
}
})
testEmbed({
embed: mixdropScraper,
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
streams: 1,
}
})

View File

@ -0,0 +1,102 @@
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';
export interface TestSourceOptions {
source: Sourcerer;
testSuite: ScrapeMedia[];
types: TestTypes[];
debug?: boolean;
expect: {
embeds?: number;
streams?: number;
error?: boolean;
notfound?: boolean;
}
}
function makeBaseProviders() {
const builder = buildProviders()
.setTarget(targets.ANY)
.setFetcher(makeStandardFetcher(fetch));
const embeds = getBuiltinEmbeds();
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");
describe(`source:${ops.source.id}`, () => {
ops.testSuite.forEach((test) => {
describe(`test ${test.title}`, () => {
async function runTest(providers: ProviderControls) {
let hasNotFound = false;
let hasError = false;
let streamCount = 0;
let embedCount = 0;
let embeds = [];
try {
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;
}
expect(ops.expect.error ?? false).toBe(hasError);
expect(ops.expect.notfound ?? false).toBe(hasNotFound);
expect(ops.expect.streams ?? 0).toBe(streamCount);
expect(ops.expect.embeds ?? 0).toBe(embedCount);
}
if (ops.types.includes('standard')) {
it(`standard`, async () => {
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();
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");
const providers = makeBaseProviders()
.addSource(ops.source)
.setProxiedFetcher(makeSimpleProxyFetcher(process.env.MOVIE_WEB_PROXY_URL, fetch))
.build();
await runTest(providers);
})
}
})
})
})
}

View File

@ -0,0 +1,95 @@
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";
dotenv.config();
testSource({
source: lookmovieScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['ip:standard'],
expect: {
streams: 1,
}
})
testSource({
source: showboxScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
}
})
testSource({
source: flixhqScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
}
})
testSource({
source: goMoviesScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
}
})
testSource({
source: smashyStreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
}
})
testSource({
source: vidsrcScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 1,
}
})
testSource({
source: vidSrcToScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 2,
}
})
testSource({
source: zoechipScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
embeds: 3,
}
})
testSource({
source: remotestreamScraper,
testSuite: [testMedia.arcane, testMedia.hamilton],
types: ['standard', 'proxied'],
expect: {
streams: 1,
}
})

View File

@ -0,0 +1,30 @@
import { ScrapeMedia } from "@/entrypoint/utils/media";
function makeMedia(media: ScrapeMedia): ScrapeMedia {
return media;
}
export const testMedia = {
arcane: makeMedia({
type: "show",
title: "Arcane",
tmdbId: "94605",
releaseYear: 2021,
episode: {
number: 1,
tmdbId: '1953812',
},
season: {
number: 1,
tmdbId: '134187',
},
imdbId: 'tt11126994'
}),
hamilton: makeMedia({
type: 'movie',
tmdbId: '556574',
imdbId: 'tt8503618',
releaseYear: 2020,
title: 'Hamilton'
})
}

View File

@ -2,7 +2,7 @@
import { vi } from 'vitest'; import { vi } from 'vitest';
import { gatherAllEmbeds, gatherAllSources } from '@/providers/all'; import { gatherAllEmbeds, gatherAllSources } from '@/providers/all';
import { Embed, Sourcerer } from '@/providers/base'; import { makeEmbed, makeSourcerer } from '@/providers/base';
export function makeProviderMocks() { export function makeProviderMocks() {
const embedsMock = vi.fn<Parameters<typeof gatherAllEmbeds>, ReturnType<typeof gatherAllEmbeds>>(); const embedsMock = vi.fn<Parameters<typeof gatherAllEmbeds>, ReturnType<typeof gatherAllEmbeds>>();
@ -13,104 +13,104 @@ export function makeProviderMocks() {
}; };
} }
const sourceA = { const sourceA = makeSourcerer({
id: 'a', id: 'a',
name: 'A', name: 'A',
rank: 1, rank: 1,
disabled: false, disabled: false,
flags: [], flags: [],
} as Sourcerer; });
const sourceB = { const sourceB = makeSourcerer({
id: 'b', id: 'b',
name: 'B', name: 'B',
rank: 2, rank: 2,
disabled: false, disabled: false,
flags: [], flags: [],
} as Sourcerer; });
const sourceCDisabled = { const sourceCDisabled = makeSourcerer({
id: 'c', id: 'c',
name: 'C', name: 'C',
rank: 3, rank: 3,
disabled: true, disabled: true,
flags: [], flags: [],
} as Sourcerer; });
const sourceAHigherRank = { const sourceAHigherRank = makeSourcerer({
id: 'a', id: 'a',
name: 'A', name: 'A',
rank: 100, rank: 100,
disabled: false, disabled: false,
flags: [], flags: [],
} as Sourcerer; });
const sourceGSameRankAsA = { const sourceGSameRankAsA = makeSourcerer({
id: 'g', id: 'g',
name: 'G', name: 'G',
rank: 1, rank: 1,
disabled: false, disabled: false,
flags: [], flags: [],
} as Sourcerer; });
const fullSourceYMovie = { const fullSourceYMovie = makeSourcerer({
id: 'y', id: 'y',
name: 'Y', name: 'Y',
rank: 105, rank: 105,
scrapeMovie: vi.fn(), scrapeMovie: vi.fn(),
flags: [], flags: [],
} as Sourcerer; });
const fullSourceYShow = { const fullSourceYShow = makeSourcerer({
id: 'y', id: 'y',
name: 'Y', name: 'Y',
rank: 105, rank: 105,
scrapeShow: vi.fn(), scrapeShow: vi.fn(),
flags: [], flags: [],
} as Sourcerer; });
const fullSourceZBoth = { const fullSourceZBoth = makeSourcerer({
id: 'z', id: 'z',
name: 'Z', name: 'Z',
rank: 106, rank: 106,
scrapeMovie: vi.fn(), scrapeMovie: vi.fn(),
scrapeShow: vi.fn(), scrapeShow: vi.fn(),
flags: [], flags: [],
} as Sourcerer; });
const embedD = { const embedD = makeEmbed({
id: 'd', id: 'd',
rank: 4, rank: 4,
disabled: false, disabled: false,
} as Embed; } as any);
const embedA = { const embedA = makeEmbed({
id: 'a', id: 'a',
rank: 5, rank: 5,
disabled: false, disabled: false,
} as Embed; } as any);
const embedEDisabled = { const embedEDisabled = makeEmbed({
id: 'e', id: 'e',
rank: 6, rank: 6,
disabled: true, disabled: true,
} as Embed; } as any);
const embedDHigherRank = { const embedDHigherRank = makeEmbed({
id: 'd', id: 'd',
rank: 4000, rank: 4000,
disabled: false, disabled: false,
} as Embed; } as any);
const embedFSameRankAsA = { const embedFSameRankAsA = makeEmbed({
id: 'f', id: 'f',
rank: 5, rank: 5,
disabled: false, disabled: false,
} as Embed; } as any);
const embedHSameRankAsSourceA = { const embedHSameRankAsSourceA = makeEmbed({
id: 'h', id: 'h',
rank: 1, rank: 1,
disabled: false, disabled: false,
} as Embed; } as any);
const fullEmbedX = { const fullEmbedX = makeEmbed({
id: 'x', id: 'x',
name: 'X', name: 'X',
rank: 104, rank: 104,
} as Embed; } as any);
const fullEmbedZ = { const fullEmbedZ = makeEmbed({
id: 'z', id: 'z',
name: 'Z', name: 'Z',
rank: 109, rank: 109,
} as Embed; } as any);
export const mockSources = { export const mockSources = {
sourceA, sourceA,

View File

@ -1,4 +1,4 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests'; import { mockEmbeds, mockSources } from '../providerTests';
import { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers'; import { getBuiltinEmbeds, getBuiltinSources } from '@/entrypoint/providers';
import { FeatureMap } from '@/entrypoint/utils/targets'; import { FeatureMap } from '@/entrypoint/utils/targets';
import { getProviders } from '@/providers/get'; import { getProviders } from '@/providers/get';

View File

@ -1,4 +1,4 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests'; import { mockEmbeds, mockSources } from '../providerTests.ts';
import { makeProviders } from '@/entrypoint/declare'; import { makeProviders } from '@/entrypoint/declare';
import { targets } from '@/entrypoint/utils/targets'; import { targets } from '@/entrypoint/utils/targets';
import { afterEach, describe, expect, it, vi } from 'vitest'; import { afterEach, describe, expect, it, vi } from 'vitest';

View File

@ -1,4 +1,4 @@
import { mockEmbeds, mockSources } from '@/__test__/providerTests'; import { mockEmbeds, mockSources } from '../providerTests.ts';
import { makeProviders } from '@/entrypoint/declare'; import { makeProviders } from '@/entrypoint/declare';
import { targets } from '@/entrypoint/utils/targets'; import { targets } from '@/entrypoint/utils/targets';
import { afterEach, describe, expect, it, vi } from 'vitest'; import { afterEach, describe, expect, it, vi } from 'vitest';

View File

@ -5,6 +5,10 @@ export const flags = {
// the stream is locked on IP, so only works if // the stream is locked on IP, so only works if
// request maker is same as player (not compatible with proxies) // request maker is same as player (not compatible with proxies)
IP_LOCKED: 'ip-locked', IP_LOCKED: 'ip-locked',
// The source/embed is blocking cloudflare ip's
// This flag is not compatible with a proxy hosted on cloudflare
CF_BLOCKED: 'cf-blocked',
} as const; } as const;
export type Flags = (typeof flags)[keyof typeof flags]; export type Flags = (typeof flags)[keyof typeof flags];

View File

@ -2,6 +2,7 @@ import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base'; import { makeEmbed } from '@/providers/base';
import { parseInputUrl } from '@/providers/embeds/febbox/common'; import { parseInputUrl } from '@/providers/embeds/febbox/common';
import { getStreamQualities } from '@/providers/embeds/febbox/qualities'; import { getStreamQualities } from '@/providers/embeds/febbox/qualities';
import { getSubtitles } from '@/providers/embeds/febbox/subtitles';
export const febboxMp4Scraper = makeEmbed({ export const febboxMp4Scraper = makeEmbed({
id: 'febbox-mp4', id: 'febbox-mp4',
@ -41,7 +42,7 @@ export const febboxMp4Scraper = makeEmbed({
stream: [ stream: [
{ {
id: 'primary', id: 'primary',
captions: [], // subtitles temporarily disabled, the endpoints are broken captions: await getSubtitles(ctx, id, fid, type, episode, season),
qualities, qualities,
type: 'file', type: 'file',
flags: [flags.CORS_ALLOWED], flags: [flags.CORS_ALLOWED],

View File

@ -4,6 +4,9 @@ import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base'; import { makeEmbed } from '@/providers/base';
import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions'; import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions';
const origin = 'https://rabbitstream.net';
const referer = 'https://rabbitstream.net/';
const { AES, enc } = crypto; const { AES, enc } = crypto;
interface StreamRes { interface StreamRes {
@ -126,6 +129,10 @@ export const upcloudScraper = makeEmbed({
playlist: sources.file, playlist: sources.file,
flags: [flags.CORS_ALLOWED], flags: [flags.CORS_ALLOWED],
captions, captions,
preferredHeaders: {
Referer: referer,
Origin: origin,
},
}, },
], ],
}; };

View File

@ -2,15 +2,14 @@ import { makeFullUrl } from '@/fetchers/common';
import { decodeData } from '@/providers/sources/vidsrcto/common'; import { decodeData } from '@/providers/sources/vidsrcto/common';
import { EmbedScrapeContext } from '@/utils/context'; import { EmbedScrapeContext } from '@/utils/context';
export const vidplayBase = 'https://vidplay.site'; export const vidplayBase = 'https://vidplay.online';
export const referer = `${vidplayBase}/`;
// This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16 // This file is based on https://github.com/Ciarands/vidsrc-to-resolver/blob/dffa45e726a4b944cb9af0c9e7630476c93c0213/vidsrc.py#L16
// Full credits to @Ciarands! // Full credits to @Ciarands!
export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise<string[]> => { export const getDecryptionKeys = async (ctx: EmbedScrapeContext): Promise<string[]> => {
const res = await ctx.fetcher<string>( const res = await ctx.fetcher<string>('https://raw.githubusercontent.com/Ciarands/vidsrc-keys/main/keys.json');
'https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json',
);
return JSON.parse(res); return JSON.parse(res);
}; };

View File

@ -1,7 +1,7 @@
import { makeEmbed } from '@/providers/base'; import { makeEmbed } from '@/providers/base';
import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions'; import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions';
import { getFileUrl } from './common'; import { getFileUrl, referer } from './common';
import { SubtitleResult, VidplaySourceResponse } from './types'; import { SubtitleResult, VidplaySourceResponse } from './types';
export const vidplayScraper = makeEmbed({ export const vidplayScraper = makeEmbed({
@ -46,6 +46,10 @@ export const vidplayScraper = makeEmbed({
playlist: source, playlist: source,
flags: [], flags: [],
captions, captions,
preferredHeaders: {
Referer: referer,
Origin: referer,
},
}, },
], ],
}; };

View File

@ -4,6 +4,14 @@ import { makeEmbed } from '@/providers/base';
const hlsURLRegex = /file:"(.*?)"/; const hlsURLRegex = /file:"(.*?)"/;
const setPassRegex = /var pass_path = "(.*set_pass\.php.*)";/; const setPassRegex = /var pass_path = "(.*set_pass\.php.*)";/;
function formatHlsB64(data: string): string {
const encodedB64 = data.replace(/\/@#@\/[^=/]+==/g, '');
if (encodedB64.match(/\/@#@\/[^=/]+==/)) {
return formatHlsB64(encodedB64);
}
return encodedB64;
}
export const vidsrcembedScraper = makeEmbed({ export const vidsrcembedScraper = makeEmbed({
id: 'vidsrcembed', // VidSrc is both a source and an embed host id: 'vidsrcembed', // VidSrc is both a source and an embed host
name: 'VidSrc', name: 'VidSrc',
@ -15,13 +23,12 @@ export const vidsrcembedScraper = makeEmbed({
}, },
}); });
const match = html // When this eventually breaks see the player js @ pjs_main.js
.match(hlsURLRegex)?.[1] // If you know what youre doing and are slightly confused about how to reverse this feel free to reach out to ciaran_ds on discord with any queries
?.replace(/(\/\/\S+?=)/g, '') let hlsMatch = html.match(hlsURLRegex)?.[1]?.slice(2);
.replace('#2', ''); if (!hlsMatch) throw new Error('Unable to find HLS playlist');
if (!match) throw new Error('Unable to find HLS playlist'); hlsMatch = formatHlsB64(hlsMatch);
const finalUrl = atob(match); const finalUrl = atob(hlsMatch);
if (!finalUrl.includes('.m3u8')) throw new Error('Unable to find HLS playlist'); if (!finalUrl.includes('.m3u8')) throw new Error('Unable to find HLS playlist');
let setPassLink = html.match(setPassRegex)?.[1]; let setPassLink = html.match(setPassRegex)?.[1];

View File

@ -32,7 +32,7 @@ async function universalScraper(ctx: MovieScrapeContext | ShowScrapeContext): Pr
export const lookmovieScraper = makeSourcerer({ export const lookmovieScraper = makeSourcerer({
id: 'lookmovie', id: 'lookmovie',
name: 'LookMovie', name: 'LookMovie',
rank: 1, rank: 700,
flags: [flags.IP_LOCKED], flags: [flags.IP_LOCKED],
scrapeShow: universalScraper, scrapeShow: universalScraper,
scrapeMovie: universalScraper, scrapeMovie: universalScraper,

View File

@ -4,6 +4,9 @@ import { NotFoundError } from '@/utils/errors';
const remotestreamBase = atob('aHR0cHM6Ly9mc2IuOG1ldDNkdGpmcmNxY2hjb25xcGtsd3hzeGIyb2N1bWMuc3RyZWFt'); const remotestreamBase = atob('aHR0cHM6Ly9mc2IuOG1ldDNkdGpmcmNxY2hjb25xcGtsd3hzeGIyb2N1bWMuc3RyZWFt');
const origin = 'https://remotestre.am';
const referer = 'https://remotestre.am/';
export const remotestreamScraper = makeSourcerer({ export const remotestreamScraper = makeSourcerer({
id: 'remotestream', id: 'remotestream',
name: 'Remote Stream', name: 'Remote Stream',
@ -16,9 +19,12 @@ export const remotestreamScraper = makeSourcerer({
const playlistLink = `${remotestreamBase}/Shows/${ctx.media.tmdbId}/${seasonNumber}/${episodeNumber}/${episodeNumber}.m3u8`; const playlistLink = `${remotestreamBase}/Shows/${ctx.media.tmdbId}/${seasonNumber}/${episodeNumber}/${episodeNumber}.m3u8`;
ctx.progress(30); ctx.progress(30);
const streamRes = await ctx.fetcher.full(playlistLink, { const streamRes = await ctx.proxiedFetcher.full(playlistLink, {
method: 'HEAD', method: 'GET',
readHeaders: ['content-type'], readHeaders: ['content-type'],
headers: {
Referer: referer,
},
}); });
if (!streamRes.headers.get('content-type')?.toLowerCase().includes('application/x-mpegurl')) if (!streamRes.headers.get('content-type')?.toLowerCase().includes('application/x-mpegurl'))
throw new NotFoundError('No watchable item found'); throw new NotFoundError('No watchable item found');
@ -33,6 +39,10 @@ export const remotestreamScraper = makeSourcerer({
playlist: playlistLink, playlist: playlistLink,
type: 'hls', type: 'hls',
flags: [flags.CORS_ALLOWED], flags: [flags.CORS_ALLOWED],
preferredHeaders: {
Referer: referer,
Origin: origin,
},
}, },
], ],
}; };
@ -41,9 +51,12 @@ export const remotestreamScraper = makeSourcerer({
const playlistLink = `${remotestreamBase}/Movies/${ctx.media.tmdbId}/${ctx.media.tmdbId}.m3u8`; const playlistLink = `${remotestreamBase}/Movies/${ctx.media.tmdbId}/${ctx.media.tmdbId}.m3u8`;
ctx.progress(30); ctx.progress(30);
const streamRes = await ctx.fetcher.full(playlistLink, { const streamRes = await ctx.proxiedFetcher.full(playlistLink, {
method: 'HEAD', method: 'GET',
readHeaders: ['content-type'], readHeaders: ['content-type'],
headers: {
Referer: referer,
},
}); });
if (!streamRes.headers.get('content-type')?.toLowerCase().includes('application/x-mpegurl')) if (!streamRes.headers.get('content-type')?.toLowerCase().includes('application/x-mpegurl'))
throw new NotFoundError('No watchable item found'); throw new NotFoundError('No watchable item found');
@ -58,6 +71,10 @@ export const remotestreamScraper = makeSourcerer({
playlist: playlistLink, playlist: playlistLink,
type: 'hls', type: 'hls',
flags: [flags.CORS_ALLOWED], flags: [flags.CORS_ALLOWED],
preferredHeaders: {
Referer: referer,
Origin: origin,
},
}, },
], ],
}; };

View File

@ -42,7 +42,7 @@ export const showboxScraper = makeSourcerer({
id: 'showbox', id: 'showbox',
name: 'Showbox', name: 'Showbox',
rank: 300, rank: 300,
flags: [flags.CORS_ALLOWED], flags: [flags.CORS_ALLOWED, flags.CF_BLOCKED],
scrapeShow: comboScraper, scrapeShow: comboScraper,
scrapeMovie: comboScraper, scrapeMovie: comboScraper,
}); });

View File

@ -5,7 +5,9 @@ const dts = require('vite-plugin-dts');
const pkg = require('./package.json'); const pkg = require('./package.json');
const fs = require('fs/promises'); const fs = require('fs/promises');
const main = path.resolve(__dirname, 'src/index.ts'); const shouldTestProviders = process.env.MW_TEST_PROVIDERS === "true"
let tests = ['src/__test__/standard/**/*.test.ts'];
if (shouldTestProviders) tests = ['src/__test__/providers/**/*.test.ts']
module.exports = defineConfig({ module.exports = defineConfig({
plugins: [ plugins: [
@ -34,10 +36,13 @@ module.exports = defineConfig({
outDir: 'lib', outDir: 'lib',
lib: { lib: {
entry: main, entry: path.resolve(__dirname, 'src/index.ts'),
name: 'index', name: 'index',
fileName: 'index', fileName: 'index',
formats: ['umd', 'es'], formats: ['umd', 'es'],
}, },
}, },
test: {
include: tests
}
}); });