update deps, migrate to pnpm

This commit is contained in:
Jorrin 2024-03-29 21:23:32 +01:00
parent f8a5120064
commit 21f1fd3cee
35 changed files with 15210 additions and 25328 deletions

18571
.docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,8 @@
"build": "nuxi build",
"generate": "nuxi generate",
"preview": "nuxi preview",
"lint": "eslint ."
"lint": "eslint .",
"preinstall": "npx -y only-allow pnpm"
},
"devDependencies": {
"@nuxt-themes/docus": "^1.13.1",

10025
.docs/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

72
.eslintrc Normal file
View File

@ -0,0 +1,72 @@
{
"env": {
"browser": true
},
"extends": ["airbnb-base", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
"ignorePatterns": ["lib/*", "tests/*", "/*.js", "/*.ts", "/src/__test__/*", "/**/*.test.ts", "test/*"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"tsconfigRootDir": "./"
},
"settings": {
"import/resolver": {
"typescript": {
"project": "./tsconfig.json"
}
}
},
"plugins": ["@typescript-eslint", "import", "prettier"],
"rules": {
"no-plusplus": "off",
"class-methods-use-this": "off",
"no-bitwise": "off",
"no-underscore-dangle": "off",
"@typescript-eslint/no-explicit-any": "off",
"no-console": ["error", { "allow": ["warn", "error"] }],
"@typescript-eslint/no-this-alias": "off",
"import/prefer-default-export": "off",
"@typescript-eslint/no-empty-function": "off",
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
"no-restricted-syntax": "off",
"import/no-unresolved": ["error", { "ignore": ["^virtual:"] }],
"consistent-return": "off",
"no-continue": "off",
"no-eval": "off",
"no-await-in-loop": "off",
"no-nested-ternary": "off",
"no-param-reassign": ["error", { "props": false }],
"prefer-destructuring": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"import/extensions": [
"error",
"ignorePackages",
{
"ts": "never",
"tsx": "never"
}
],
"import/order": [
"error",
{
"groups": ["builtin", "external", "internal", ["sibling", "parent"], "index", "unknown"],
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
],
"sort-imports": [
"error",
{
"ignoreCase": false,
"ignoreDeclarationSort": true,
"ignoreMemberSort": false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"],
"allowSeparatedGroups": true
}
]
}
}

View File

@ -1,72 +0,0 @@
module.exports = {
env: {
browser: true,
},
extends: ['airbnb-base', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
ignorePatterns: ['lib/*', 'tests/*', '/*.js', '/*.ts', '/src/__test__/*', '/**/*.test.ts', 'test/*'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: './',
},
settings: {
'import/resolver': {
typescript: {
project: './tsconfig.json',
},
},
},
plugins: ['@typescript-eslint', 'import', 'prettier'],
rules: {
'no-plusplus': 'off',
'class-methods-use-this': 'off',
'no-bitwise': 'off',
'no-underscore-dangle': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'no-console': ['error', { allow: ['warn', 'error'] }],
'@typescript-eslint/no-this-alias': 'off',
'import/prefer-default-export': 'off',
'@typescript-eslint/no-empty-function': 'off',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'no-restricted-syntax': 'off',
'import/no-unresolved': ['error', { ignore: ['^virtual:'] }],
'consistent-return': 'off',
'no-continue': 'off',
'no-eval': 'off',
'no-await-in-loop': 'off',
'no-nested-ternary': 'off',
'no-param-reassign': ['error', { props: false }],
'prefer-destructuring': 'off',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'import/extensions': [
'error',
'ignorePackages',
{
ts: 'never',
tsx: 'never',
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', ['sibling', 'parent'], 'index', 'unknown'],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
'sort-imports': [
'error',
{
ignoreCase: false,
ignoreDeclarationSort: true,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
allowSeparatedGroups: true,
},
],
},
};

View File

@ -11,28 +11,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2
with:
version: 8
- name: Install packages
working-directory: ./.docs
run: npm install
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Build project
working-directory: ./.docs
run: npm run generate
env:
- name: Install packages
working-directory: ./.docs
run: pnpm install
- name: Build project
working-directory: ./.docs
run: pnpm run generate
env:
NUXT_APP_BASE_URL: /providers/
- name: Upload production-ready build files
uses: actions/upload-pages-artifact@v1
with:
path: ./.docs/.output/public
- name: Upload production-ready build files
uses: actions/upload-pages-artifact@v1
with:
path: ./.docs/.output/public
deploy:
name: Deploy

View File

@ -14,16 +14,21 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
- uses: pnpm/action-setup@v2
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
version: 8
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
registry-url: "https://registry.npmjs.org"
- name: Install packages
run: npm ci
run: pnpm install --frozen-lockfile
- name: Publish
run: npm publish --access public
run: pnpm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -13,22 +13,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- uses: pnpm/action-setup@v2
with:
version: 8
- name: Install packages
run: npm install
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Run tests
run: npm run test
- name: Install packages
run: pnpm install --frozen-lockfile
- name: Run integration tests
run: npm run build && npm run test:integration
- name: Run tests
run: pnpm run test
- name: Run linting
run: npm run lint
- name: Run integration tests
run: pnpm run build && pnpm run test:integration
- name: Run linting
run: pnpm run lint

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ node_modules/
/lib
coverage
.env
.eslintcache

6045
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
"name": "@movie-web/providers",
"version": "2.2.5",
"description": "Package that contains all the providers of movie-web",
"type": "module",
"main": "./lib/index.umd.js",
"types": "./lib/index.d.ts",
"files": [
@ -10,12 +11,12 @@
"exports": {
".": {
"import": {
"types": "./lib/index.d.mts",
"default": "./lib/index.mjs"
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"require": {
"types": "./lib/index.d.ts",
"default": "./lib/index.umd.js"
"default": "./lib/index.umd.cjs"
}
}
},
@ -35,7 +36,7 @@
"homepage": "https://movie-web.github.io/providers/",
"scripts": {
"build": "vite build && tsc --noEmit",
"cli": "ts-node ./src/dev-cli/index.ts",
"cli": "vite-node ./src/dev-cli/index.ts",
"test": "vitest run",
"test:watch": "vitest",
"test:providers": "cross-env MW_TEST_PROVIDERS=true vitest run --reporter verbose",
@ -44,50 +45,51 @@
"lint": "eslint --ext .ts,.js src/",
"lint:fix": "eslint --fix --ext .ts,.js src/",
"lint:report": "eslint --ext .ts,.js --output-file eslint_report.json --format json src/",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run lint"
"preinstall": "npx -y only-allow pnpm",
"prepare": "pnpm run build",
"prepublishOnly": "pnpm test && pnpm run lint"
},
"devDependencies": {
"@nabla/vite-plugin-eslint": "^2.0.2",
"@types/cookie": "^0.6.0",
"@types/crypto-js": "^4.1.1",
"@types/node-fetch": "^2.6.6",
"@types/randombytes": "^2.0.1",
"@types/crypto-js": "^4.2.2",
"@types/node-fetch": "^2.6.11",
"@types/randombytes": "^2.0.3",
"@types/set-cookie-parser": "^2.4.7",
"@types/spinnies": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"@vitest/coverage-v8": "^0.34.3",
"commander": "^11.0.0",
"@types/spinnies": "^0.5.3",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@vitest/coverage-v8": "^1.4.0",
"commander": "^12.0.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"dotenv": "^16.4.5",
"enquirer": "^2.4.1",
"eslint": "^8.30.0",
"eslint": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"node-fetch": "^2.7.0",
"prettier": "^2.6.2",
"puppeteer": "^21.6.1",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"node-fetch": "^3.3.2",
"prettier": "^3.2.5",
"puppeteer": "^22.6.1",
"spinnies": "^0.5.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.6.7",
"tsc-alias": "^1.8.8",
"tsconfig-paths": "^4.2.0",
"typescript": "^4.6.3",
"vite": "^4.0.0",
"vite-plugin-dts": "^3.5.3",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^0.32.2"
"typescript": "^5.4.3",
"vite": "^5.2.7",
"vite-node": "^1.4.0",
"vite-plugin-dts": "^3.8.1",
"vitest": "^1.4.0"
},
"dependencies": {
"cheerio": "^1.0.0-rc.12",
"cookie": "^0.6.0",
"crypto-js": "^4.1.1",
"crypto-js": "^4.2.0",
"form-data": "^4.0.0",
"iso-639-1": "^3.1.0",
"nanoid": "^3.3.6",
"node-fetch": "^2.7.0",
"iso-639-1": "^3.1.2",
"nanoid": "^3.3.7",
"node-fetch": "^3.3.2",
"set-cookie-parser": "^2.6.0",
"unpacker": "^1.0.1"
}

4346
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
});
}
}
})
})
})
});
});
});
}

View File

@ -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,
}
})
},
});

View File

@ -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);
})
});
}
})
})
})
});
});
});
}

View File

@ -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,
}
})
},
});

View File

@ -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',
}),
};

View File

@ -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,
});
});
});

View File

@ -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',
);
});
});

View File

@ -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 });
expectFetchCall({
inputUrl: "https://google.com/",
input: {
method: "POST",
query: {},
readHeaders: [],
headers: {},
},
outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
output: {
method: "POST",
headers: {},
},
outputBody: { hello: 42 }
})
outputBody: 'hello world',
});
// setResult("json", { hello: 42 });
// expectFetchCall({
// inputUrl: "https://google.com/",
// input: {
// method: "POST",
// query: {},
// readHeaders: [],
// headers: {},
// },
// outputUrl: `https://example.com/proxy?destination=${encodeURIComponent('https://google.com/')}`,
// output: {
// method: "POST",
// headers: {},
// },
// outputBody: { hello: 42 }
// })
});
});

View File

@ -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 },
});
});
});

View File

@ -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],
});

View File

@ -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,
);
});
});

View File

@ -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,7 +18,7 @@ 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);
});
@ -28,26 +28,26 @@ describe('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);
});

View File

@ -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);
});
});

View File

@ -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));
}

View File

@ -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 });

View File

@ -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: [

View File

@ -1,4 +1,4 @@
import { makeProviders, makeStandardFetcher, targets } from '../../lib/index.mjs';
import { makeProviders, makeStandardFetcher, targets } from '../../lib/index.js';
(window as any).TEST = () => {
makeProviders({

View File

@ -29,3 +29,4 @@ try {
}
console.log('Success!');
process.exit(0);

View File

@ -1,2 +1,2 @@
require('../../lib/index.umd');
require('../../lib/index.umd.cjs');
console.log('import successful!');

View File

@ -1,2 +1,2 @@
import '../../lib/index.mjs';
import '../../lib/index.js';
console.log('import successful!');

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"target": "es2021",
"lib": ["es2021", "DOM"],
"module": "CommonJS",
"module": "ESNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"declaration": true,
"outDir": "./lib",
@ -11,14 +12,12 @@
"experimentalDecorators": true,
"isolatedModules": false,
"skipLibCheck": true,
"resolveJsonModule": true,
"paths": {
"@/*": ["./*"],
"@entrypoint": ["./index.ts"]
}
},
"include": ["src"],
"exclude": ["node_modules", "**/__test__"],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
"include": ["src", "vite.config.ts"],
"exclude": ["node_modules", "**/__test__"]
}

View File

@ -1,48 +0,0 @@
const path = require('path');
const { defineConfig } = require('vitest/config');
const { default: eslint } = require('vite-plugin-eslint');
const dts = require('vite-plugin-dts');
const pkg = require('./package.json');
const fs = require('fs/promises');
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({
plugins: [
eslint(),
dts({
rollupTypes: true,
async afterBuild() {
const filePath = path.join(__dirname, './lib/index.d.ts');
await fs.writeFile(filePath.replace('.d.ts', '.d.mts'), await fs.readFile(filePath, 'utf-8'), 'utf-8');
},
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
minify: false,
rollupOptions: {
external: Object.keys(pkg.dependencies),
output: {
globals: Object.fromEntries(Object.keys(pkg.dependencies).map((v) => [v, v])),
},
},
outDir: 'lib',
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'index',
fileName: 'index',
formats: ['umd', 'es'],
},
},
test: {
include: tests
}
});

42
vite.config.ts Normal file
View File

@ -0,0 +1,42 @@
import path from 'path';
import { defineConfig } from 'vitest/config';
import eslintPlugin from '@nabla/vite-plugin-eslint';
import dts from 'vite-plugin-dts';
import pkg from './package.json';
const shouldTestProviders = process.env.MW_TEST_PROVIDERS === 'true';
let tests: string[] = ['src/__test__/standard/**/*.test.ts'];
if (shouldTestProviders) tests = ['src/__test__/providers/**/*.test.ts'];
export default defineConfig({
plugins: [
eslintPlugin({}),
dts({
rollupTypes: true,
}),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
minify: false,
rollupOptions: {
external: Object.keys(pkg.dependencies),
output: {
globals: Object.fromEntries(Object.keys(pkg.dependencies).map((v) => [v, v])),
},
},
outDir: 'lib',
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'index',
fileName: 'index',
formats: ['umd', 'es'],
},
},
test: {
include: tests,
},
});