commit
3d0207a0db
|
@ -16,6 +16,7 @@ import { vTubeScraper } from '@/providers/embeds/vtube';
|
||||||
import { astraScraper, novaScraper } from '@/providers/embeds/whvx';
|
import { astraScraper, novaScraper } from '@/providers/embeds/whvx';
|
||||||
import { autoembedScraper } from '@/providers/sources/autoembed';
|
import { autoembedScraper } from '@/providers/sources/autoembed';
|
||||||
import { catflixScraper } from '@/providers/sources/catflix';
|
import { catflixScraper } from '@/providers/sources/catflix';
|
||||||
|
import { ee3Scraper } from '@/providers/sources/ee3';
|
||||||
import { flixhqScraper } from '@/providers/sources/flixhq/index';
|
import { flixhqScraper } from '@/providers/sources/flixhq/index';
|
||||||
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
|
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
|
||||||
import { insertunitScraper } from '@/providers/sources/insertunit';
|
import { insertunitScraper } from '@/providers/sources/insertunit';
|
||||||
|
@ -93,6 +94,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
||||||
soaperTvScraper,
|
soaperTvScraper,
|
||||||
autoembedScraper,
|
autoembedScraper,
|
||||||
tugaflixScraper,
|
tugaflixScraper,
|
||||||
|
ee3Scraper,
|
||||||
whvxScraper,
|
whvxScraper,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const useAltEndpoint: boolean = false;
|
||||||
|
|
||||||
|
export const baseUrl = useAltEndpoint ? 'https://rips.cc' : 'https://ee3.me';
|
||||||
|
|
||||||
|
export const username = '_sf_';
|
||||||
|
|
||||||
|
export const password = 'defonotscraping';
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { flags } from '@/entrypoint/utils/targets';
|
||||||
|
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||||
|
import { Caption } from '@/providers/captions';
|
||||||
|
import { compareMedia } from '@/utils/compare';
|
||||||
|
import { MovieScrapeContext } from '@/utils/context';
|
||||||
|
import { makeCookieHeader } from '@/utils/cookie';
|
||||||
|
import { NotFoundError } from '@/utils/errors';
|
||||||
|
|
||||||
|
import { baseUrl, password, username } from './common';
|
||||||
|
import { itemDetails, renewResponse } from './types';
|
||||||
|
import { login, parseSearch } from './utils';
|
||||||
|
|
||||||
|
// this source only has movies
|
||||||
|
async function comboScraper(ctx: MovieScrapeContext): Promise<SourcererOutput> {
|
||||||
|
const pass = await login(username, password, ctx);
|
||||||
|
if (!pass) throw new Error('Login failed');
|
||||||
|
|
||||||
|
const search = parseSearch(
|
||||||
|
await ctx.proxiedFetcher<string>('/get', {
|
||||||
|
baseUrl,
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({ query: ctx.media.title, action: 'search' }),
|
||||||
|
headers: {
|
||||||
|
cookie: makeCookieHeader({ PHPSESSID: pass }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const id = search.find((v) => v && compareMedia(ctx.media, v.title, v.year))?.id;
|
||||||
|
if (!id) throw new NotFoundError('No watchable item found');
|
||||||
|
|
||||||
|
const details: itemDetails = JSON.parse(
|
||||||
|
await ctx.proxiedFetcher<string>('/get', {
|
||||||
|
baseUrl,
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({ id, action: 'get_movie_info' }),
|
||||||
|
headers: {
|
||||||
|
cookie: makeCookieHeader({ PHPSESSID: pass }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (!details.message.video) throw new Error('Failed to get the stream');
|
||||||
|
|
||||||
|
const keyParams: renewResponse = JSON.parse(
|
||||||
|
await ctx.proxiedFetcher<string>('/renew', {
|
||||||
|
baseUrl,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
cookie: makeCookieHeader({ PHPSESSID: pass }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (!keyParams.k) throw new Error('Failed to get the key');
|
||||||
|
|
||||||
|
const server = details.message.server === '1' ? 'https://vid.ee3.me/vid/' : 'https://vault.rips.cc/video/';
|
||||||
|
const k = keyParams.k;
|
||||||
|
const url = `${server}${details.message.video}?${new URLSearchParams({ k })}`;
|
||||||
|
const captions: Caption[] = [];
|
||||||
|
|
||||||
|
// this how they actually deal with subtitles
|
||||||
|
if (details.message.subs?.toLowerCase() === 'yes' && details.message.imdbID) {
|
||||||
|
captions.push({
|
||||||
|
id: `https://rips.cc/subs/${details.message.imdbID}.vtt`,
|
||||||
|
url: `https://rips.cc/subs/${details.message.imdbID}.vtt`,
|
||||||
|
type: 'vtt',
|
||||||
|
hasCorsRestrictions: false,
|
||||||
|
language: 'en',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [],
|
||||||
|
stream: [
|
||||||
|
{
|
||||||
|
id: 'primary',
|
||||||
|
type: 'file',
|
||||||
|
flags: [flags.CORS_ALLOWED],
|
||||||
|
captions,
|
||||||
|
qualities: {
|
||||||
|
// should be unknown, but all the videos are 720p
|
||||||
|
720: {
|
||||||
|
type: 'mp4',
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ee3Scraper = makeSourcerer({
|
||||||
|
id: 'ee3',
|
||||||
|
name: 'EE3',
|
||||||
|
rank: 111,
|
||||||
|
flags: [flags.CORS_ALLOWED],
|
||||||
|
scrapeMovie: comboScraper,
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
export interface itemDetails {
|
||||||
|
status: number;
|
||||||
|
message: {
|
||||||
|
id: string;
|
||||||
|
imdbID: string;
|
||||||
|
title: string;
|
||||||
|
video: string;
|
||||||
|
server: string;
|
||||||
|
year: string;
|
||||||
|
image: string;
|
||||||
|
glow: string;
|
||||||
|
rating: string;
|
||||||
|
watch_count: string;
|
||||||
|
datetime?: string | null;
|
||||||
|
requested_by?: string | null;
|
||||||
|
subs?: string | null;
|
||||||
|
time?: string | null;
|
||||||
|
duration?: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface renewResponse {
|
||||||
|
k: string;
|
||||||
|
msg?: string | null;
|
||||||
|
status: number | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface loginResponse {
|
||||||
|
status: number;
|
||||||
|
message: string;
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
|
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||||
|
import { parseSetCookie } from '@/utils/cookie';
|
||||||
|
|
||||||
|
import { baseUrl } from './common';
|
||||||
|
import { loginResponse } from './types';
|
||||||
|
|
||||||
|
export async function login(
|
||||||
|
user: string,
|
||||||
|
pass: string,
|
||||||
|
ctx: ShowScrapeContext | MovieScrapeContext,
|
||||||
|
): Promise<string | null> {
|
||||||
|
const req = await ctx.proxiedFetcher.full<string>('/login', {
|
||||||
|
baseUrl,
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({ user, pass, action: 'login' }),
|
||||||
|
readHeaders: ['Set-Cookie'],
|
||||||
|
});
|
||||||
|
const res: loginResponse = JSON.parse(req.body);
|
||||||
|
|
||||||
|
const cookie = parseSetCookie(
|
||||||
|
// It retruns a cookie even when the login failed
|
||||||
|
// I have the backup cookie here just in case
|
||||||
|
res.status === 1 ? (req.headers.get('Set-Cookie') ?? '') : 'PHPSESSID=mk2p73c77qc28o5i5120843ruu;',
|
||||||
|
);
|
||||||
|
|
||||||
|
return cookie.PHPSESSID.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseSearch(body: string): { title: string; year: number; id: string }[] {
|
||||||
|
const result: { title: string; year: number; id: string }[] = [];
|
||||||
|
|
||||||
|
const $ = load(body);
|
||||||
|
$('div').each((_, element) => {
|
||||||
|
const title = $(element).find('.title').text().trim();
|
||||||
|
const year = parseInt($(element).find('.details span').first().text().trim(), 10);
|
||||||
|
const id = $(element).find('.control-buttons').attr('data-id');
|
||||||
|
|
||||||
|
if (title && year && id) {
|
||||||
|
result.push({ title, year, id });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import { load } from 'cheerio';
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
import { flags } from '@/entrypoint/utils/targets';
|
|
||||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||||
import { compareTitle } from '@/utils/compare';
|
import { compareTitle } from '@/utils/compare';
|
||||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||||
|
|
Loading…
Reference in New Issue