Merge pull request #31 from MemeCornucopia/lookmovie

Added Lookmovie Source
This commit is contained in:
mrjvs
2023-12-14 20:13:15 +01:00
committed by GitHub
7 changed files with 1490 additions and 3099 deletions

4385
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
export const flags = { export const flags = {
NO_CORS: 'no-cors', NO_CORS: 'no-cors',
IP_LOCKED: 'ip-locked',
} as const; } as const;
export type Flags = (typeof flags)[keyof typeof flags]; export type Flags = (typeof flags)[keyof typeof flags];

View File

@@ -8,6 +8,7 @@ import { upstreamScraper } from '@/providers/embeds/upstream';
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 { kissAsianScraper } from '@/providers/sources/kissasian/index'; import { kissAsianScraper } from '@/providers/sources/kissasian/index';
import { lookmovieScraper } from '@/providers/sources/lookmovie';
import { remotestreamScraper } from '@/providers/sources/remotestream'; 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';
@@ -23,6 +24,7 @@ export function gatherAllSources(): Array<Sourcerer> {
superStreamScraper, superStreamScraper,
goMoviesScraper, goMoviesScraper,
zoechipScraper, zoechipScraper,
lookmovieScraper,
showBoxScraper, showBoxScraper,
]; ];
} }

View File

@@ -0,0 +1,36 @@
import { flags } from '@/main/targets';
import { SourcererOutput, makeSourcerer } from '@/providers/base';
import { NotFoundError } from '@/utils/errors';
import { scrape, searchAndFindMedia } from './util';
import { MovieContext, ShowContext } from '../zoechip/common';
async function universalScraper(ctx: ShowContext | MovieContext): Promise<SourcererOutput> {
const lookmovieData = await searchAndFindMedia(ctx, ctx.media);
if (!lookmovieData) throw new NotFoundError('Media not found');
ctx.progress(30);
const videoUrl = await scrape(ctx, ctx.media, lookmovieData);
if (!videoUrl) throw new NotFoundError('No video found');
ctx.progress(60);
return {
embeds: [],
stream: {
playlist: videoUrl,
type: 'hls',
flags: [flags.IP_LOCKED],
captions: [],
},
};
}
export const lookmovieScraper = makeSourcerer({
id: 'lookmovie',
name: 'LookMovie',
rank: 1,
flags: [flags.IP_LOCKED],
scrapeShow: universalScraper,
scrapeMovie: universalScraper,
});

View File

@@ -0,0 +1,60 @@
import { MovieMedia } from '@/main/media';
// ! Types
interface BaseConfig {
/** The website's slug. Formatted as `1839578-person-of-interest-2011` */
slug: string;
/** Type of request */
type: 'show' | 'movie';
/** Hash */
hash: string;
/** Hash expiry */
expires: number;
}
interface TvConfig extends BaseConfig {
/** Type of request */
type: 'show';
/** The episode ID for a TV show. Given in search and URL */
episodeId: string;
}
interface MovieConfig extends BaseConfig {
/** Type of request */
type: 'movie';
/** Movie's id */
id_movie: string;
}
export type Config = MovieConfig | TvConfig;
export interface episodeObj {
season: string;
episode: string;
id: string;
}
export interface ShowDataResult {
episodes: episodeObj[];
}
interface VideoSources {
[key: string]: string;
}
export interface StreamsDataResult {
streams: VideoSources;
}
export interface ResultItem {
title: string;
slug: string;
year: string;
id_movie: string;
id_show: string;
}
export interface Result {
title(media: MovieMedia, title: any, arg2: number): boolean;
year(year: any): number | undefined;
id_movie: any;
id_show: string;
items: ResultItem[];
}

View File

@@ -0,0 +1,59 @@
import { MovieMedia, ShowMedia } from '@/main/media';
import { compareMedia } from '@/utils/compare';
import { ScrapeContext } from '@/utils/context';
import { NotFoundError } from '@/utils/errors';
import { Result, ResultItem, ShowDataResult, episodeObj } from './type';
import { getVideoUrl } from './video';
export async function searchAndFindMedia(
ctx: ScrapeContext,
media: MovieMedia | ShowMedia,
): Promise<ResultItem | undefined> {
if (media.type === 'show') {
const searchRes = await ctx.fetcher<Result>(`/v1/shows`, {
baseUrl: 'https://lmscript.xyz',
query: { 'filters[q]': media.title },
});
const results = searchRes.items;
const result = results.find((res: ResultItem) => compareMedia(media, res.title, Number(res.year)));
return result;
}
if (media.type === 'movie') {
const searchRes = await ctx.fetcher<Result>(`/v1/movies`, {
baseUrl: 'https://lmscript.xyz',
query: { 'filters[q]': media.title },
});
const results = searchRes.items;
const result = results.find((res: ResultItem) => compareMedia(media, res.title, Number(res.year)));
return result;
}
}
export async function scrape(ctx: ScrapeContext, media: MovieMedia | ShowMedia, result: ResultItem) {
// Find the relevant id
let id = null;
if (media.type === 'movie') {
id = result.id_movie;
} else if (media.type === 'show') {
const data = await ctx.fetcher<ShowDataResult>(`/v1/shows`, {
baseUrl: 'https://lmscript.xyz',
query: { expand: 'episodes', id: result.id_show },
});
const episode = data.episodes?.find((v: episodeObj) => {
return Number(v.season) === Number(media.season.number) && Number(v.episode) === Number(media.episode.number);
});
if (episode) id = episode.id;
}
// Check ID
if (id === null) throw new NotFoundError('Not found');
const videoUrl = await getVideoUrl(ctx, id, media);
return videoUrl;
}

View File

@@ -0,0 +1,46 @@
import { MovieMedia, ShowMedia } from '@/main/media';
import { ScrapeContext } from '@/utils/context';
import { StreamsDataResult } from './type';
export async function getVideoSources(
ctx: ScrapeContext,
id: string,
media: MovieMedia | ShowMedia,
): Promise<StreamsDataResult> {
// Fetch video sources
let path = '';
if (media.type === 'show') {
path = `/v1/episodes/view`;
} else if (media.type === 'movie') {
path = `/v1/movies/view`;
}
const data = await ctx.fetcher<StreamsDataResult>(path, {
baseUrl: 'https://lmscript.xyz',
query: { expand: 'streams', id },
});
return data;
}
export async function getVideoUrl(
ctx: ScrapeContext,
id: string,
media: MovieMedia | ShowMedia,
): Promise<string | null> {
// Get sources
const data = await getVideoSources(ctx, id, media);
const videoSources = data.streams;
// Find video URL and return it
const opts = ['auto', '1080p', '1080', '720p', '720', '480p', '480', '240p', '240', '360p', '360', '144', '144p'];
let videoUrl: string | null = null;
for (const res of opts) {
if (videoSources[res] && !videoUrl) {
videoUrl = videoSources[res];
}
}
return videoUrl;
}