mirror of
https://github.com/movie-web/providers.git
synced 2025-09-13 18:13:25 +00:00
Add lookmovie
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -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"
|
||||||
},
|
},
|
||||||
|
@@ -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"
|
||||||
|
@@ -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> {
|
||||||
|
31
src/providers/sources/lookmovie/index.ts
Normal file
31
src/providers/sources/lookmovie/index.ts
Normal 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,
|
||||||
|
});
|
31
src/providers/sources/lookmovie/type.ts
Normal file
31
src/providers/sources/lookmovie/type.ts
Normal 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;
|
||||||
|
}
|
68
src/providers/sources/lookmovie/util.ts
Normal file
68
src/providers/sources/lookmovie/util.ts
Normal 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;
|
||||||
|
}
|
31
src/providers/sources/lookmovie/video.ts
Normal file
31
src/providers/sources/lookmovie/video.ts
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user