From a0637cc04696d319e999c1f6cdad94fa49ad69c3 Mon Sep 17 00:00:00 2001 From: Isra Date: Fri, 15 Sep 2023 15:09:00 -0500 Subject: [PATCH] Add kissasian scraper --- README.md | 2 +- src/providers/all.ts | 3 +- src/providers/sources/kissasian/common.ts | 15 ++++ src/providers/sources/kissasian/getEmbeds.ts | 34 +++++++++ .../sources/kissasian/getEpisodes.ts | 14 ++++ src/providers/sources/kissasian/index.ts | 70 +++++++++++++++++++ src/providers/sources/kissasian/search.ts | 27 +++++++ 7 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 src/providers/sources/kissasian/common.ts create mode 100644 src/providers/sources/kissasian/getEmbeds.ts create mode 100644 src/providers/sources/kissasian/getEpisodes.ts create mode 100644 src/providers/sources/kissasian/index.ts create mode 100644 src/providers/sources/kissasian/search.ts diff --git a/README.md b/README.md index ba636ce..b99ee2a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ providers need to be ported to the new provider repo: * [ ] superstream * [x] flixhq * [ ] gomovies -* [ ] kissasian +* [x] kissasian * [x] remotestream embeds: diff --git a/src/providers/all.ts b/src/providers/all.ts index 3b980f4..6e2a0b1 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -3,11 +3,12 @@ import { mp4uploadScraper } from '@/providers/embeds/mp4upload'; import { streamsbScraper } from '@/providers/embeds/streamsb'; import { upcloudScraper } from '@/providers/embeds/upcloud'; import { flixhqScraper } from '@/providers/sources/flixhq/index'; +import { kissAsianScraper } from '@/providers/sources/kissasian/index'; import { remotestreamScraper } from '@/providers/sources/remotestream'; export function gatherAllSources(): Array { // all sources are gathered here - return [flixhqScraper, remotestreamScraper]; + return [flixhqScraper, remotestreamScraper, kissAsianScraper]; } export function gatherAllEmbeds(): Array { diff --git a/src/providers/sources/kissasian/common.ts b/src/providers/sources/kissasian/common.ts new file mode 100644 index 0000000..d9e35e0 --- /dev/null +++ b/src/providers/sources/kissasian/common.ts @@ -0,0 +1,15 @@ +import { mp4uploadScraper } from '@/providers/embeds/mp4upload'; +import { streamsbScraper } from '@/providers/embeds/streamsb'; + +export const kissasianBase = 'https://kissasian.li'; + +export const embedProviders = [ + { + type: mp4uploadScraper.id, + id: 'mp', + }, + { + type: streamsbScraper.id, + id: 'sb', + }, +]; diff --git a/src/providers/sources/kissasian/getEmbeds.ts b/src/providers/sources/kissasian/getEmbeds.ts new file mode 100644 index 0000000..4fb28f9 --- /dev/null +++ b/src/providers/sources/kissasian/getEmbeds.ts @@ -0,0 +1,34 @@ +import { load } from 'cheerio'; + +import { ScrapeContext } from '@/utils/context'; + +import { embedProviders, kissasianBase } from './common'; + +export async function getEmbeds( + ctx: ScrapeContext, + targetEpisode: { + number: string; + url?: string; + }, +) { + let embeds = await Promise.all( + embedProviders.map(async (provider) => { + const watch = await ctx.fetcher(`${kissasianBase}/${targetEpisode.url}&s=${provider.id}`); + + const watchPage = load(watch); + + const embedUrl = watchPage('iframe[id=my_video_1]').attr('src'); + + if (!embedUrl) throw new Error('Embed not found'); + + return { + embedId: provider.id, + url: embedUrl, + }; + }), + ); + + embeds = embeds.filter((e) => !!e.url); + + return embeds; +} diff --git a/src/providers/sources/kissasian/getEpisodes.ts b/src/providers/sources/kissasian/getEpisodes.ts new file mode 100644 index 0000000..4d94848 --- /dev/null +++ b/src/providers/sources/kissasian/getEpisodes.ts @@ -0,0 +1,14 @@ +import { CheerioAPI } from 'cheerio'; + +export async function getEpisodes(dramaPage: CheerioAPI) { + const episodesEl = dramaPage('tbody tr:not(:first-child)'); + + return episodesEl + .toArray() + .map((ep) => { + const number = dramaPage(ep).find('td.episodeSub a').text().split('Episode')[1]?.trim(); + const url = dramaPage(ep).find('td.episodeSub a').attr('href'); + return { number, url }; + }) + .filter((e) => !!e.url); +} diff --git a/src/providers/sources/kissasian/index.ts b/src/providers/sources/kissasian/index.ts new file mode 100644 index 0000000..5c09236 --- /dev/null +++ b/src/providers/sources/kissasian/index.ts @@ -0,0 +1,70 @@ +import { load } from 'cheerio'; + +import { flags } from '@/main/targets'; +import { makeSourcerer } from '@/providers/base'; +import { NotFoundError } from '@/utils/errors'; + +import { getEmbeds } from './getEmbeds'; +import { getEpisodes } from './getEpisodes'; +import { search } from './search'; + +export const kissAsianScraper = makeSourcerer({ + id: 'kissasian', + name: 'KissAsian', + rank: 130, + flags: [flags.NO_CORS], + + async scrapeShow(ctx) { + const seasonNumber = ctx.media.season.number; + const episodeNumber = ctx.media.episode.number; + + const dramas = await search(ctx, ctx.media.title, seasonNumber); + + const targetDrama = dramas.find((d) => d.name?.toLowerCase() === ctx.media.title.toLowerCase()) ?? dramas[0]; + if (!targetDrama) throw new Error('Drama not found'); + + ctx.progress(30); + + const drama = await ctx.fetcher(targetDrama.url); + + const dramaPage = load(drama); + + const episodes = await getEpisodes(dramaPage); + + const targetEpisode = episodes.find((e) => e.number === `${episodeNumber}`); + if (!targetEpisode?.url) throw new NotFoundError('Episode not found'); + + ctx.progress(70); + + const embeds = await getEmbeds(ctx, targetEpisode); + + return { + embeds, + }; + }, + async scrapeMovie(ctx) { + const dramas = await search(ctx, ctx.media.title, undefined); + + const targetDrama = dramas.find((d) => d.name?.toLowerCase() === ctx.media.title.toLowerCase()) ?? dramas[0]; + if (!targetDrama) throw new Error('Drama not found'); + + ctx.progress(30); + + const drama = await ctx.fetcher(targetDrama.url); + + const dramaPage = load(drama); + + const episodes = await getEpisodes(dramaPage); + + const targetEpisode = episodes[0]; + if (!targetEpisode?.url) throw new NotFoundError('Episode not found'); + + ctx.progress(70); + + const embeds = await getEmbeds(ctx, targetEpisode); + + return { + embeds, + }; + }, +}); diff --git a/src/providers/sources/kissasian/search.ts b/src/providers/sources/kissasian/search.ts new file mode 100644 index 0000000..5d913f5 --- /dev/null +++ b/src/providers/sources/kissasian/search.ts @@ -0,0 +1,27 @@ +import { load } from 'cheerio'; +import FormData from 'form-data'; + +import { ScrapeContext } from '@/utils/context'; + +import { kissasianBase } from './common'; + +export async function search(ctx: ScrapeContext, title: string, seasonNumber?: number) { + const searchForm = new FormData(); + searchForm.append('keyword', `${title} ${seasonNumber ?? ''}`.trim()); + searchForm.append('type', 'Drama'); + + const searchResults = await ctx.fetcher('/Search/SearchSuggest', { + baseUrl: kissasianBase, + method: 'POST', + body: searchForm, + }); + + const searchPage = load(searchResults); + + return Array.from(searchPage('a')).map((drama) => { + return { + name: searchPage(drama).text(), + url: drama.attribs.href, + }; + }); +}