Add lookmovie

This commit is contained in:
Jip Fr
2023-10-01 22:07:37 +02:00
parent 09679b2585
commit bbc4f60c73
7 changed files with 175 additions and 4 deletions

6
package-lock.json generated
View File

@@ -1,17 +1,18 @@
{ {
"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",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"json5": "^2.2.3",
"nanoid": "^3.3.6", "nanoid": "^3.3.6",
"node-fetch": "^2.7.0", "node-fetch": "^2.7.0",
"unpacker": "^1.0.1" "unpacker": "^1.0.1"
@@ -4434,7 +4435,6 @@
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"bin": { "bin": {
"json5": "lib/cli.js" "json5": "lib/cli.js"
}, },

View File

@@ -79,6 +79,7 @@
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"json5": "^2.2.3",
"nanoid": "^3.3.6", "nanoid": "^3.3.6",
"node-fetch": "^2.7.0", "node-fetch": "^2.7.0",
"unpacker": "^1.0.1" "unpacker": "^1.0.1"

View File

@@ -7,13 +7,22 @@ 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';
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,
lookmovieScraper,
];
} }
export function gatherAllEmbeds(): Array<Embed> { export function gatherAllEmbeds(): Array<Embed> {

View File

@@ -0,0 +1,31 @@
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.media);
if (!lookmovieData) throw new NotFoundError('Media not found');
const videoUrl = await scrape(ctx.media, lookmovieData);
if (!videoUrl) throw new NotFoundError('No video found');
return {
embeds: [],
stream: {
playlist: videoUrl,
type: 'hls',
flags: [],
},
};
}
export const lookmovieScraper = makeSourcerer({
id: 'lookmovie',
name: 'LookMovie',
rank: 1,
flags: [],
scrapeShow: universalScraper,
scrapeMovie: universalScraper,
});

View File

@@ -0,0 +1,31 @@
// ! 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 Result {
title: string;
slug: string;
year: string;
id_movie?: string;
}

View File

@@ -0,0 +1,68 @@
import json5 from 'json5';
import { MovieMedia, ShowMedia } from '@/main/media';
import { compareMedia } from '@/utils/compare';
import { NotFoundError } from '@/utils/errors';
import { Result } from './type';
import { getVideoUrl } from './video';
export async function searchAndFindMedia(media: MovieMedia | ShowMedia): Promise<Result | undefined> {
const searchRes = await fetch(
`https://lookmovie2.to/api/v1/${media.type}s/do-search/?q=${encodeURIComponent(media.title)}`,
).then((d) => d.json());
const results = searchRes.result;
const result = results.find((res: Result) => compareMedia(media, res.title, Number(res.year)));
return result;
}
export async function scrape(media: MovieMedia | ShowMedia, result: Result) {
const url = `https://www.lookmovie2.to/${media.type}s/play/${result.slug}`;
const pageReq = await fetch(url).then((d) => d.text());
// Extract and parse JSON
const scriptJson = `{${pageReq
.slice(pageReq.indexOf(`${media.type}_storage`))
.split('};')[0]
.split('= {')[1]
.trim()}}`;
const data = json5.parse(scriptJson);
// Find the relevant id
let id = null;
if (media.type === 'movie') {
id = result.id_movie;
} else if (media.type === 'show') {
const episodeObj = data.seasons.find((v: any) => {
return Number(v.season) === Number(media.season.number) && Number(v.episode) === Number(media.episode.number);
});
if (episodeObj) id = episodeObj.id_episode;
}
// Check ID
if (id === null) throw new NotFoundError('Not found');
// Generate object to send over to scraper
let reqObj = null;
if (media.type === 'show') {
reqObj = {
slug: result.slug,
episodeId: id,
type: 'tv',
...data,
};
} else if (media.type === 'movie') {
reqObj = {
slug: result.slug,
movieId: id,
type: 'movie',
...data,
};
}
if (!reqObj) throw new NotFoundError('Invalid media type');
const videoUrl = await getVideoUrl(reqObj);
return videoUrl;
}

View File

@@ -0,0 +1,31 @@
import { Config } from './type';
export async function getVideoSources(config: Config): Promise<any> {
// Fetch video sources
let url = '';
if (config.type === 'show') {
url = `https://www.lookmovie2.to/api/v1/security/episode-access?id_episode=${config.episodeId}&hash=${config.hash}&expires=${config.expires}`;
} else if (config.type === 'movie') {
url = `https://www.lookmovie2.to/api/v1/security/movie-access?id_movie=${config.id_movie}&hash=${config.hash}&expires=${config.expires}`;
}
const data = await fetch(url).then((d) => d.json());
return data;
}
export async function getVideoUrl(config: Config): Promise<string | null> {
// Get sources
const data = await getVideoSources(config);
const videoSources = data.streams;
// Find video URL and return it
const opts = ['1080p', '1080', '720p', '720', '480p', '480', 'auto'];
let videoUrl: string | null = null;
for (const res of opts) {
if (videoSources[res] && !videoUrl) {
videoUrl = videoSources[res];
}
}
return videoUrl;
}