mirror of
https://github.com/movie-web/providers.git
synced 2025-09-13 12:43:25 +00:00
Add flixhq shows
This commit is contained in:
58
package-lock.json
generated
58
package-lock.json
generated
@@ -12,12 +12,13 @@
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"crypto-js": "^4.1.1",
|
||||
"form-data": "^4.0.0",
|
||||
"nanoid": "^5.0.1",
|
||||
"node-fetch": "^2.7.0"
|
||||
"node-fetch": "^2.7.0",
|
||||
"randombytes": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node-fetch": "^2.6.6",
|
||||
"@types/randombytes": "^2.0.1",
|
||||
"@types/spinnies": "^0.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
||||
"@typescript-eslint/parser": "^5.60.0",
|
||||
@@ -942,6 +943,15 @@
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/randombytes": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.1.tgz",
|
||||
"integrity": "sha512-kWMqPyxpTUTofwbGN47MWddBFiJnWJlfLBdDg2NvmZSKHOmKY9ujVA3PIfBgXcIHTCpsqoQqYudBwanFXzGD9A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
|
||||
@@ -4698,23 +4708,6 @@
|
||||
"url": "https://github.com/sponsors/raouldeheer"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.1.tgz",
|
||||
"integrity": "sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@@ -5258,6 +5251,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
@@ -5428,6 +5429,25 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safe-regex-test": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
|
||||
|
@@ -48,6 +48,7 @@
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node-fetch": "^2.6.6",
|
||||
"@types/randombytes": "^2.0.1",
|
||||
"@types/spinnies": "^0.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
||||
"@typescript-eslint/parser": "^5.60.0",
|
||||
@@ -76,7 +77,7 @@
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"crypto-js": "^4.1.1",
|
||||
"form-data": "^4.0.0",
|
||||
"nanoid": "^5.0.1",
|
||||
"node-fetch": "^2.7.0"
|
||||
"node-fetch": "^2.7.0",
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
@@ -183,7 +183,7 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
|
||||
id: source.id,
|
||||
});
|
||||
spinnies.succeed('scrape', { text: 'Done!' });
|
||||
console.log(result);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
let message = 'Unknown error';
|
||||
if (error instanceof Error) {
|
||||
@@ -208,7 +208,7 @@ async function runScraper(providers: ProviderControls, source: MetaOutput, optio
|
||||
id: source.id,
|
||||
});
|
||||
spinnies.succeed('scrape', { text: 'Done!' });
|
||||
console.log(result);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
let message = 'Unknown error';
|
||||
if (error instanceof Error) {
|
||||
|
@@ -15,10 +15,27 @@ export const flixhqScraper = makeSourcerer({
|
||||
const id = await getFlixhqId(ctx, ctx.media);
|
||||
if (!id) throw new NotFoundError('no search results match');
|
||||
|
||||
const sources = await getFlixhqSources(ctx, id);
|
||||
const sources = await getFlixhqSources(ctx, ctx.media, id);
|
||||
const upcloudStream = sources.find((v) => v.embed.toLowerCase() === 'upcloud');
|
||||
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 getFlixhqSources(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 {
|
||||
embeds: [
|
||||
{
|
||||
|
@@ -1,16 +1,68 @@
|
||||
import { load } from 'cheerio';
|
||||
|
||||
import { MovieMedia, ShowMedia } from '@/main/media';
|
||||
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
export async function getFlixhqSources(ctx: ScrapeContext, id: string) {
|
||||
const type = id.split('/')[0];
|
||||
export async function getFlixhqSources(ctx: ScrapeContext, media: MovieMedia | ShowMedia, id: string) {
|
||||
const episodeParts = id.split('-');
|
||||
const episodeId = episodeParts[episodeParts.length - 1];
|
||||
|
||||
const data = await ctx.proxiedFetcher<string>(`/ajax/${type}/episodes/${episodeId}`, {
|
||||
if (media.type === 'show') {
|
||||
const seasonsListData = await ctx.proxiedFetcher<string>(`/ajax/season/list/${episodeId}`, {
|
||||
baseUrl: flixHqBase,
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const data = await ctx.proxiedFetcher<string>(`/ajax/movie/episodes/${episodeId}`, {
|
||||
baseUrl: flixHqBase,
|
||||
});
|
||||
|
||||
const doc = load(data);
|
||||
const sourceLinks = doc('.nav-item > a')
|
||||
.toArray()
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { load } from 'cheerio';
|
||||
|
||||
import { MovieMedia } from '@/main/media';
|
||||
import { MovieMedia, ShowMedia } from '@/main/media';
|
||||
import { flixHqBase } from '@/providers/sources/flixhq/common';
|
||||
import { compareMedia } from '@/utils/compare';
|
||||
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, '-')}`, {
|
||||
baseUrl: flixHqBase,
|
||||
});
|
||||
|
@@ -1,12 +1,11 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import randomBytes from 'randombytes';
|
||||
|
||||
import type { ScrapeContext } from '@/utils/context';
|
||||
|
||||
import { apiUrls, appId, appKey, key } from './common';
|
||||
import { encrypt, getVerify } from './crypto';
|
||||
|
||||
const nanoid = customAlphabet('0123456789abcdef', 32);
|
||||
const expiry = () => Math.floor(Date.now() / 1000 + 60 * 60 * 12);
|
||||
|
||||
export const sendRequest = async (ctx: ScrapeContext, data: object, altApi = false) => {
|
||||
@@ -40,7 +39,7 @@ export const sendRequest = async (ctx: ScrapeContext, data: object, altApi = fal
|
||||
formatted.append('platform', 'android');
|
||||
formatted.append('version', '129');
|
||||
formatted.append('medium', 'Website');
|
||||
formatted.append('token', nanoid());
|
||||
formatted.append('token', randomBytes(16).toString('hex'));
|
||||
|
||||
const requestUrl = altApi ? apiUrls[1] : apiUrls[0];
|
||||
|
||||
|
@@ -12,8 +12,14 @@ export function compareTitle(a: string, b: string): boolean {
|
||||
return normalizeTitle(a) === normalizeTitle(b);
|
||||
}
|
||||
|
||||
export function compareMedia(media: CommonMedia, title: string, releaseYear?: number): boolean {
|
||||
export function compareMedia(media: CommonMedia, title: string, releaseYear?: number, compareYear?: boolean): boolean {
|
||||
// if no year is provided, count as if its the correct year
|
||||
const isSameYear = releaseYear === undefined ? true : media.releaseYear === releaseYear;
|
||||
let isSameYear: boolean;
|
||||
if (!compareYear) {
|
||||
isSameYear = true;
|
||||
} else {
|
||||
isSameYear = releaseYear === undefined ? true : media.releaseYear === releaseYear;
|
||||
}
|
||||
|
||||
return compareTitle(media.title, title) && isSameYear;
|
||||
}
|
||||
|
Reference in New Issue
Block a user