mirror of
https://github.com/movie-web/providers.git
synced 2025-09-13 12:43:25 +00:00
70
.docs/package-lock.json
generated
70
.docs/package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "docus-starter",
|
"name": "providers-docs",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "docus-starter",
|
"name": "providers-docs",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt-themes/docus": "^1.13.1",
|
"@nuxt-themes/docus": "^1.13.1",
|
||||||
@@ -157,12 +157,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.22.15",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||||
"integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==",
|
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.22.15",
|
"@babel/types": "^7.23.0",
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"@jridgewell/trace-mapping": "^0.3.17",
|
"@jridgewell/trace-mapping": "^0.3.17",
|
||||||
"jsesc": "^2.5.1"
|
"jsesc": "^2.5.1"
|
||||||
@@ -241,22 +241,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-environment-visitor": {
|
"node_modules/@babel/helper-environment-visitor": {
|
||||||
"version": "7.22.5",
|
"version": "7.22.20",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-function-name": {
|
"node_modules/@babel/helper-function-name": {
|
||||||
"version": "7.22.5",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.22.5",
|
"@babel/template": "^7.22.15",
|
||||||
"@babel/types": "^7.22.5"
|
"@babel/types": "^7.23.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -401,9 +401,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
"version": "7.22.19",
|
"version": "7.22.20",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.19.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||||
"integrity": "sha512-Tinq7ybnEPFFXhlYOYFiSjespWQk0dq2dRNAiMdRTOYQzEGqnnNyrTxPYHP5r6wGjlF1rFgABdDV0g8EwD6Qbg==",
|
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -494,9 +494,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.22.16",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||||
"integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==",
|
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@@ -638,19 +638,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.22.19",
|
"version": "7.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.19.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||||
"integrity": "sha512-ZCcpVPK64krfdScRbpxF6xA5fz7IOsfMwx1tcACvCzt6JY+0aHkBk7eIU8FRDSZRU5Zei6Z4JfgAxN1bqXGECg==",
|
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.22.13",
|
"@babel/code-frame": "^7.22.13",
|
||||||
"@babel/generator": "^7.22.15",
|
"@babel/generator": "^7.23.0",
|
||||||
"@babel/helper-environment-visitor": "^7.22.5",
|
"@babel/helper-environment-visitor": "^7.22.20",
|
||||||
"@babel/helper-function-name": "^7.22.5",
|
"@babel/helper-function-name": "^7.23.0",
|
||||||
"@babel/helper-hoist-variables": "^7.22.5",
|
"@babel/helper-hoist-variables": "^7.22.5",
|
||||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||||
"@babel/parser": "^7.22.16",
|
"@babel/parser": "^7.23.0",
|
||||||
"@babel/types": "^7.22.19",
|
"@babel/types": "^7.23.0",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
},
|
},
|
||||||
@@ -668,13 +668,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.22.19",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||||
"integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==",
|
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.22.5",
|
"@babel/helper-string-parser": "^7.22.5",
|
||||||
"@babel/helper-validator-identifier": "^7.22.19",
|
"@babel/helper-validator-identifier": "^7.22.20",
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -13812,9 +13812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.29",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
"integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
|
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@movie-web/providers",
|
"name": "@movie-web/providers",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@movie-web/providers",
|
"name": "@movie-web/providers",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
@@ -2183,9 +2183,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/crypto-js": {
|
"node_modules/crypto-js": {
|
||||||
"version": "4.1.1",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||||
},
|
},
|
||||||
"node_modules/css-select": {
|
"node_modules/css-select": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
@@ -5116,9 +5116,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.24",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@movie-web/providers",
|
"name": "@movie-web/providers",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"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",
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
|
/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
|
||||||
|
|
||||||
|
import util from 'node:util';
|
||||||
|
|
||||||
import { program } from 'commander';
|
import { program } from 'commander';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { prompt } from 'enquirer';
|
import { prompt } from 'enquirer';
|
||||||
@@ -45,6 +47,10 @@ if (!TMDB_API_KEY?.trim()) {
|
|||||||
throw new Error('Missing MOVIE_WEB_TMDB_API_KEY environment variable');
|
throw new Error('Missing MOVIE_WEB_TMDB_API_KEY environment variable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logDeepObject(object: Record<any, any>) {
|
||||||
|
console.log(util.inspect(object, { showHidden: false, depth: null, colors: true }));
|
||||||
|
}
|
||||||
|
|
||||||
function getAllSources() {
|
function getAllSources() {
|
||||||
// * The only way to get a list of all sources is to
|
// * The only way to get a list of all sources is to
|
||||||
// * create all these things. Maybe this should change
|
// * create all these things. Maybe this should change
|
||||||
@@ -181,7 +187,7 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
|
|||||||
id: source.id,
|
id: source.id,
|
||||||
});
|
});
|
||||||
spinnies.succeed('scrape', { text: 'Done!' });
|
spinnies.succeed('scrape', { text: 'Done!' });
|
||||||
console.log(result);
|
logDeepObject(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let message = 'Unknown error';
|
let message = 'Unknown error';
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
@@ -206,7 +212,7 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
|
|||||||
id: source.id,
|
id: source.id,
|
||||||
});
|
});
|
||||||
spinnies.succeed('scrape', { text: 'Done!' });
|
spinnies.succeed('scrape', { text: 'Done!' });
|
||||||
console.log(result);
|
logDeepObject(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let message = 'Unknown error';
|
let message = 'Unknown error';
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
@@ -6,6 +6,7 @@ import { EmbedOutput, SourcererOutput } from '@/providers/base';
|
|||||||
import { ProviderList } from '@/providers/get';
|
import { ProviderList } from '@/providers/get';
|
||||||
import { ScrapeContext } from '@/utils/context';
|
import { ScrapeContext } from '@/utils/context';
|
||||||
import { NotFoundError } from '@/utils/errors';
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
import { isValidStream } from '@/utils/valid';
|
||||||
|
|
||||||
export type IndividualSourceRunnerOptions = {
|
export type IndividualSourceRunnerOptions = {
|
||||||
features: FeatureMap;
|
features: FeatureMap;
|
||||||
@@ -50,7 +51,7 @@ export async function scrapeInvidualSource(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// stream doesn't satisfy the feature flags, so gets removed in output
|
// stream doesn't satisfy the feature flags, so gets removed in output
|
||||||
if (output?.stream && !flagsAllowedInFeatures(ops.features, output.stream.flags)) {
|
if (output?.stream && (!isValidStream(output.stream) || !flagsAllowedInFeatures(ops.features, output.stream.flags))) {
|
||||||
output.stream = undefined;
|
output.stream = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +88,9 @@ export async function scrapeIndividualEmbed(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!isValidStream(output.stream)) throw new NotFoundError('stream is incomplete');
|
||||||
if (!flagsAllowedInFeatures(ops.features, output.stream.flags))
|
if (!flagsAllowedInFeatures(ops.features, output.stream.flags))
|
||||||
throw new NotFoundError("stream doesn't satisfy target feature flags");
|
throw new NotFoundError("stream doesn't satisfy target feature flags");
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import { Stream } from '@/providers/streams';
|
|||||||
import { ScrapeContext } from '@/utils/context';
|
import { ScrapeContext } from '@/utils/context';
|
||||||
import { NotFoundError } from '@/utils/errors';
|
import { NotFoundError } from '@/utils/errors';
|
||||||
import { reorderOnIdList } from '@/utils/list';
|
import { reorderOnIdList } from '@/utils/list';
|
||||||
|
import { isValidStream } from '@/utils/valid';
|
||||||
|
|
||||||
export type RunOutput = {
|
export type RunOutput = {
|
||||||
sourceId: string;
|
sourceId: string;
|
||||||
@@ -79,6 +80,9 @@ export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOpt
|
|||||||
...contextBase,
|
...contextBase,
|
||||||
media: ops.media,
|
media: ops.media,
|
||||||
});
|
});
|
||||||
|
if (!isValidStream(output?.stream)) {
|
||||||
|
throw new NotFoundError('stream is incomplete');
|
||||||
|
}
|
||||||
if (output?.stream && !flagsAllowedInFeatures(ops.features, output.stream.flags)) {
|
if (output?.stream && !flagsAllowedInFeatures(ops.features, output.stream.flags)) {
|
||||||
throw new NotFoundError("stream doesn't satisfy target feature flags");
|
throw new NotFoundError("stream doesn't satisfy target feature flags");
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { Embed, Sourcerer } from '@/providers/base';
|
import { Embed, Sourcerer } from '@/providers/base';
|
||||||
|
import { febBoxScraper } from '@/providers/embeds/febBox';
|
||||||
import { mixdropScraper } from '@/providers/embeds/mixdrop';
|
import { mixdropScraper } from '@/providers/embeds/mixdrop';
|
||||||
import { mp4uploadScraper } from '@/providers/embeds/mp4upload';
|
import { mp4uploadScraper } from '@/providers/embeds/mp4upload';
|
||||||
import { streamsbScraper } from '@/providers/embeds/streamsb';
|
import { streamsbScraper } from '@/providers/embeds/streamsb';
|
||||||
@@ -11,12 +12,22 @@ import { remotestreamScraper } from '@/providers/sources/remotestream';
|
|||||||
import { superStreamScraper } from '@/providers/sources/superstream/index';
|
import { superStreamScraper } from '@/providers/sources/superstream/index';
|
||||||
import { zoechipScraper } from '@/providers/sources/zoechip';
|
import { zoechipScraper } from '@/providers/sources/zoechip';
|
||||||
|
|
||||||
|
import { showBoxScraper } from './sources/showbox';
|
||||||
|
|
||||||
export function gatherAllSources(): Array<Sourcerer> {
|
export function gatherAllSources(): Array<Sourcerer> {
|
||||||
// all sources are gathered here
|
// all sources are gathered here
|
||||||
return [flixhqScraper, remotestreamScraper, kissAsianScraper, superStreamScraper, goMoviesScraper, zoechipScraper];
|
return [
|
||||||
|
flixhqScraper,
|
||||||
|
remotestreamScraper,
|
||||||
|
kissAsianScraper,
|
||||||
|
superStreamScraper,
|
||||||
|
goMoviesScraper,
|
||||||
|
zoechipScraper,
|
||||||
|
showBoxScraper,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherAllEmbeds(): Array<Embed> {
|
export function gatherAllEmbeds(): Array<Embed> {
|
||||||
// all embeds are gathered here
|
// all embeds are gathered here
|
||||||
return [upcloudScraper, mp4uploadScraper, streamsbScraper, upstreamScraper, mixdropScraper];
|
return [upcloudScraper, mp4uploadScraper, streamsbScraper, upstreamScraper, febBoxScraper, mixdropScraper];
|
||||||
}
|
}
|
||||||
|
73
src/providers/embeds/febBox.ts
Normal file
73
src/providers/embeds/febBox.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { flags } from '@/main/targets';
|
||||||
|
import { makeEmbed } from '@/providers/base';
|
||||||
|
import { StreamFile } from '@/providers/streams';
|
||||||
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
|
const febBoxBase = `https://www.febbox.com`;
|
||||||
|
|
||||||
|
const allowedQualities = ['360', '480', '720', '1080'];
|
||||||
|
|
||||||
|
export const febBoxScraper = makeEmbed({
|
||||||
|
id: 'febbox',
|
||||||
|
name: 'FebBox',
|
||||||
|
rank: 160,
|
||||||
|
async scrape(ctx) {
|
||||||
|
const shareKey = ctx.url.split('/')[4];
|
||||||
|
const streams = await ctx.proxiedFetcher<{
|
||||||
|
data?: {
|
||||||
|
file_list?: {
|
||||||
|
fid?: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
}>('/file/file_share_list', {
|
||||||
|
headers: {
|
||||||
|
'accept-language': 'en', // without this header, the request is marked as a webscraper
|
||||||
|
},
|
||||||
|
baseUrl: febBoxBase,
|
||||||
|
query: {
|
||||||
|
share_key: shareKey,
|
||||||
|
pwd: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fid = streams?.data?.file_list?.[0]?.fid;
|
||||||
|
if (!fid) throw new NotFoundError('no result found');
|
||||||
|
|
||||||
|
const formParams = new URLSearchParams();
|
||||||
|
formParams.append('fid', fid);
|
||||||
|
formParams.append('share_key', shareKey);
|
||||||
|
|
||||||
|
const player = await ctx.proxiedFetcher<string>('/file/player', {
|
||||||
|
baseUrl: febBoxBase,
|
||||||
|
body: formParams,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'accept-language': 'en', // without this header, the request is marked as a webscraper
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourcesMatch = player?.match(/var sources = (\[[^\]]+\]);/);
|
||||||
|
const qualities = sourcesMatch ? JSON.parse(sourcesMatch[0].replace('var sources = ', '').replace(';', '')) : null;
|
||||||
|
|
||||||
|
const embedQualities: Record<string, StreamFile> = {};
|
||||||
|
|
||||||
|
qualities.forEach((quality: { file: string; label: string }) => {
|
||||||
|
const normalizedLabel = quality.label.toLowerCase().replace('p', '');
|
||||||
|
if (allowedQualities.includes(normalizedLabel)) {
|
||||||
|
if (!quality.file) return;
|
||||||
|
embedQualities[normalizedLabel] = {
|
||||||
|
type: 'mp4',
|
||||||
|
url: quality.file,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stream: {
|
||||||
|
type: 'file',
|
||||||
|
flags: [flags.NO_CORS],
|
||||||
|
qualities: embedQualities,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
@@ -1,7 +1,7 @@
|
|||||||
import { flags } from '@/main/targets';
|
import { flags } from '@/main/targets';
|
||||||
import { makeSourcerer } from '@/providers/base';
|
import { makeSourcerer } from '@/providers/base';
|
||||||
import { upcloudScraper } from '@/providers/embeds/upcloud';
|
import { upcloudScraper } from '@/providers/embeds/upcloud';
|
||||||
import { getFlixhqSourceDetails, getFlixhqSources } from '@/providers/sources/flixhq/scrape';
|
import { getFlixhqMovieSources, getFlixhqShowSources, getFlixhqSourceDetails } from '@/providers/sources/flixhq/scrape';
|
||||||
import { getFlixhqId } from '@/providers/sources/flixhq/search';
|
import { getFlixhqId } from '@/providers/sources/flixhq/search';
|
||||||
import { NotFoundError } from '@/utils/errors';
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
@@ -15,10 +15,27 @@ export const flixhqScraper = makeSourcerer({
|
|||||||
const id = await getFlixhqId(ctx, ctx.media);
|
const id = await getFlixhqId(ctx, ctx.media);
|
||||||
if (!id) throw new NotFoundError('no search results match');
|
if (!id) throw new NotFoundError('no search results match');
|
||||||
|
|
||||||
const sources = await getFlixhqSources(ctx, id);
|
const sources = await getFlixhqMovieSources(ctx, ctx.media, id);
|
||||||
const upcloudStream = sources.find((v) => v.embed.toLowerCase() === 'upcloud');
|
const upcloudStream = sources.find((v) => v.embed.toLowerCase() === 'upcloud');
|
||||||
if (!upcloudStream) throw new NotFoundError('upcloud stream not found for flixhq');
|
if (!upcloudStream) throw new NotFoundError('upcloud stream not found for flixhq');
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
embedId: upcloudScraper.id,
|
||||||
|
url: await getFlixhqSourceDetails(ctx, upcloudStream.episodeId),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async scrapeShow(ctx) {
|
||||||
|
const id = await getFlixhqId(ctx, ctx.media);
|
||||||
|
if (!id) throw new NotFoundError('no search results match');
|
||||||
|
|
||||||
|
const sources = await getFlixhqShowSources(ctx, ctx.media, id);
|
||||||
|
const upcloudStream = sources.find((v) => v.embed.toLowerCase() === 'server upcloud');
|
||||||
|
if (!upcloudStream) throw new NotFoundError('upcloud stream not found for flixhq');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
|
@@ -1,16 +1,26 @@
|
|||||||
import { load } from 'cheerio';
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
|
import { MovieMedia, ShowMedia } from '@/main/media';
|
||||||
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
||||||
import { ScrapeContext } from '@/utils/context';
|
import { ScrapeContext } from '@/utils/context';
|
||||||
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
export async function getFlixhqSources(ctx: ScrapeContext, id: string) {
|
export async function getFlixhqSourceDetails(ctx: ScrapeContext, sourceId: string): Promise<string> {
|
||||||
const type = id.split('/')[0];
|
const jsonData = await ctx.proxiedFetcher<Record<string, any>>(`/ajax/sources/${sourceId}`, {
|
||||||
|
baseUrl: flixHqBase,
|
||||||
|
});
|
||||||
|
|
||||||
|
return jsonData.link;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFlixhqMovieSources(ctx: ScrapeContext, media: MovieMedia, id: string) {
|
||||||
const episodeParts = id.split('-');
|
const episodeParts = id.split('-');
|
||||||
const episodeId = episodeParts[episodeParts.length - 1];
|
const episodeId = episodeParts[episodeParts.length - 1];
|
||||||
|
|
||||||
const data = await ctx.proxiedFetcher<string>(`/ajax/${type}/episodes/${episodeId}`, {
|
const data = await ctx.proxiedFetcher<string>(`/ajax/movie/episodes/${episodeId}`, {
|
||||||
baseUrl: flixHqBase,
|
baseUrl: flixHqBase,
|
||||||
});
|
});
|
||||||
|
|
||||||
const doc = load(data);
|
const doc = load(data);
|
||||||
const sourceLinks = doc('.nav-item > a')
|
const sourceLinks = doc('.nav-item > a')
|
||||||
.toArray()
|
.toArray()
|
||||||
@@ -28,10 +38,55 @@ export async function getFlixhqSources(ctx: ScrapeContext, id: string) {
|
|||||||
return sourceLinks;
|
return sourceLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFlixhqSourceDetails(ctx: ScrapeContext, sourceId: string): Promise<string> {
|
export async function getFlixhqShowSources(ctx: ScrapeContext, media: ShowMedia, id: string) {
|
||||||
const jsonData = await ctx.proxiedFetcher<Record<string, any>>(`/ajax/sources/${sourceId}`, {
|
const episodeParts = id.split('-');
|
||||||
|
const episodeId = episodeParts[episodeParts.length - 1];
|
||||||
|
|
||||||
|
const seasonsListData = await ctx.proxiedFetcher<string>(`/ajax/season/list/${episodeId}`, {
|
||||||
baseUrl: flixHqBase,
|
baseUrl: flixHqBase,
|
||||||
});
|
});
|
||||||
|
|
||||||
return jsonData.link;
|
const seasonsDoc = load(seasonsListData);
|
||||||
|
const season = seasonsDoc('.dropdown-item')
|
||||||
|
.toArray()
|
||||||
|
.find((el) => seasonsDoc(el).text() === `Season ${media.season.number}`)?.attribs['data-id'];
|
||||||
|
|
||||||
|
if (!season) throw new NotFoundError('season not found');
|
||||||
|
|
||||||
|
const seasonData = await ctx.proxiedFetcher<string>(`/ajax/season/episodes/${season}`, {
|
||||||
|
baseUrl: flixHqBase,
|
||||||
|
});
|
||||||
|
const seasonDoc = load(seasonData);
|
||||||
|
const episode = seasonDoc('.nav-item > a')
|
||||||
|
.toArray()
|
||||||
|
.map((el) => {
|
||||||
|
return {
|
||||||
|
id: seasonDoc(el).attr('data-id'),
|
||||||
|
title: seasonDoc(el).attr('title'),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.find((e) => e.title?.startsWith(`Eps ${media.episode.number}`))?.id;
|
||||||
|
|
||||||
|
if (!episode) throw new NotFoundError('episode not found');
|
||||||
|
|
||||||
|
const data = await ctx.proxiedFetcher<string>(`/ajax/episode/servers/${episode}`, {
|
||||||
|
baseUrl: flixHqBase,
|
||||||
|
});
|
||||||
|
|
||||||
|
const doc = load(data);
|
||||||
|
|
||||||
|
const sourceLinks = doc('.nav-item > a')
|
||||||
|
.toArray()
|
||||||
|
.map((el) => {
|
||||||
|
const query = doc(el);
|
||||||
|
const embedTitle = query.attr('title');
|
||||||
|
const linkId = query.attr('data-id');
|
||||||
|
if (!embedTitle || !linkId) throw new Error('invalid sources');
|
||||||
|
return {
|
||||||
|
embed: embedTitle,
|
||||||
|
episodeId: linkId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return sourceLinks;
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { load } from 'cheerio';
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
import { MovieMedia } from '@/main/media';
|
import { MovieMedia, ShowMedia } from '@/main/media';
|
||||||
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
||||||
import { compareMedia } from '@/utils/compare';
|
import { compareMedia } from '@/utils/compare';
|
||||||
import { ScrapeContext } from '@/utils/context';
|
import { ScrapeContext } from '@/utils/context';
|
||||||
|
|
||||||
export async function getFlixhqId(ctx: ScrapeContext, media: MovieMedia): Promise<string | null> {
|
export async function getFlixhqId(ctx: ScrapeContext, media: MovieMedia | ShowMedia): Promise<string | null> {
|
||||||
const searchResults = await ctx.proxiedFetcher<string>(`/search/${media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
|
const searchResults = await ctx.proxiedFetcher<string>(`/search/${media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, {
|
||||||
baseUrl: flixHqBase,
|
baseUrl: flixHqBase,
|
||||||
});
|
});
|
||||||
@@ -23,7 +23,7 @@ export async function getFlixhqId(ctx: ScrapeContext, media: MovieMedia): Promis
|
|||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
year: +year,
|
year: parseInt(year, 10),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
63
src/providers/sources/showbox/index.ts
Normal file
63
src/providers/sources/showbox/index.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
|
import { flags } from '@/main/targets';
|
||||||
|
import { makeSourcerer } from '@/providers/base';
|
||||||
|
import { febBoxScraper } from '@/providers/embeds/febBox';
|
||||||
|
import { compareMedia } from '@/utils/compare';
|
||||||
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
|
const showboxBase = `https://www.showbox.media`;
|
||||||
|
|
||||||
|
export const showBoxScraper = makeSourcerer({
|
||||||
|
id: 'show_box',
|
||||||
|
name: 'ShowBox',
|
||||||
|
rank: 20,
|
||||||
|
flags: [flags.NO_CORS],
|
||||||
|
async scrapeMovie(ctx) {
|
||||||
|
const search = await ctx.proxiedFetcher<string>('/search', {
|
||||||
|
baseUrl: showboxBase,
|
||||||
|
query: {
|
||||||
|
keyword: ctx.media.title,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchPage = load(search);
|
||||||
|
const result = searchPage('.film-name > a')
|
||||||
|
.toArray()
|
||||||
|
.map((el) => {
|
||||||
|
const titleContainer = el.parent?.parent;
|
||||||
|
if (!titleContainer) return;
|
||||||
|
const year = searchPage(titleContainer).find('.fdi-item').first().text();
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: el.attribs.title,
|
||||||
|
path: el.attribs.href,
|
||||||
|
year: !year.includes('SS') ? parseInt(year, 10) : undefined,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.find((v) => v && compareMedia(ctx.media, v.title, v.year ? v.year : undefined));
|
||||||
|
|
||||||
|
if (!result?.path) throw new NotFoundError('no result found');
|
||||||
|
|
||||||
|
const febboxResult = await ctx.proxiedFetcher<{
|
||||||
|
data?: { link?: string };
|
||||||
|
}>('/index/share_link', {
|
||||||
|
baseUrl: showboxBase,
|
||||||
|
query: {
|
||||||
|
id: result.path.split('/')[3],
|
||||||
|
type: '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!febboxResult?.data?.link) throw new NotFoundError('no result found');
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
embedId: febBoxScraper.id,
|
||||||
|
url: febboxResult.data.link,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
@@ -3,7 +3,7 @@ import { ScrapeContext } from '@/utils/context';
|
|||||||
|
|
||||||
import { sendRequest } from './sendRequest';
|
import { sendRequest } from './sendRequest';
|
||||||
|
|
||||||
import { allowedQualities } from '.';
|
const allowedQualities = ['360', '480', '720', '1080'];
|
||||||
|
|
||||||
export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) {
|
export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) {
|
||||||
const mediaRes: { list: { path: string; real_quality: string }[] } = (await sendRequest(ctx, apiQuery)).data;
|
const mediaRes: { list: { path: string; real_quality: string }[] } = (await sendRequest(ctx, apiQuery)).data;
|
||||||
@@ -20,7 +20,7 @@ export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) {
|
|||||||
|
|
||||||
allowedQualities.forEach((quality) => {
|
allowedQualities.forEach((quality) => {
|
||||||
const foundQuality = qualityMap.find((q) => q.quality === quality);
|
const foundQuality = qualityMap.find((q) => q.quality === quality);
|
||||||
if (foundQuality) {
|
if (foundQuality && foundQuality.url) {
|
||||||
qualities[quality] = {
|
qualities[quality] = {
|
||||||
type: 'mp4',
|
type: 'mp4',
|
||||||
url: foundQuality.url,
|
url: foundQuality.url,
|
||||||
|
@@ -6,8 +6,6 @@ import { NotFoundError } from '@/utils/errors';
|
|||||||
import { getStreamQualities } from './getStreamQualities';
|
import { getStreamQualities } from './getStreamQualities';
|
||||||
import { sendRequest } from './sendRequest';
|
import { sendRequest } from './sendRequest';
|
||||||
|
|
||||||
export const allowedQualities = ['360', '480', '720', '1080'];
|
|
||||||
|
|
||||||
export const superStreamScraper = makeSourcerer({
|
export const superStreamScraper = makeSourcerer({
|
||||||
id: 'superstream',
|
id: 'superstream',
|
||||||
name: 'Superstream',
|
name: 'Superstream',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
export class NotFoundError extends Error {
|
export class NotFoundError extends Error {
|
||||||
constructor(reason?: string) {
|
constructor(reason?: string) {
|
||||||
super(`Couldn't found a stream: ${reason ?? 'not found'}`);
|
super(`Couldn't find a stream: ${reason ?? 'not found'}`);
|
||||||
this.name = 'NotFoundError';
|
this.name = 'NotFoundError';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
src/utils/valid.ts
Normal file
17
src/utils/valid.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Stream } from '@/providers/streams';
|
||||||
|
|
||||||
|
export function isValidStream(stream: Stream | undefined): boolean {
|
||||||
|
if (!stream) return false;
|
||||||
|
if (stream.type === 'hls') {
|
||||||
|
if (!stream.playlist) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (stream.type === 'file') {
|
||||||
|
const validQualities = Object.values(stream.qualities).filter((v) => v.url.length > 0);
|
||||||
|
if (validQualities.length === 0) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown file type
|
||||||
|
return false;
|
||||||
|
}
|
Reference in New Issue
Block a user