commit
3d0207a0db
|
@ -16,6 +16,7 @@ import { vTubeScraper } from '@/providers/embeds/vtube';
|
|||
import { astraScraper, novaScraper } from '@/providers/embeds/whvx';
|
||||
import { autoembedScraper } from '@/providers/sources/autoembed';
|
||||
import { catflixScraper } from '@/providers/sources/catflix';
|
||||
import { ee3Scraper } from '@/providers/sources/ee3';
|
||||
import { flixhqScraper } from '@/providers/sources/flixhq/index';
|
||||
import { goMoviesScraper } from '@/providers/sources/gomovies/index';
|
||||
import { insertunitScraper } from '@/providers/sources/insertunit';
|
||||
|
@ -93,6 +94,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
soaperTvScraper,
|
||||
autoembedScraper,
|
||||
tugaflixScraper,
|
||||
ee3Scraper,
|
||||
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 { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { compareTitle } from '@/utils/compare';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
|
Loading…
Reference in New Issue