From bc64ebe23dfc4d31d6e4197490e72fa97105490f Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Wed, 7 May 2025 14:29:29 -0600 Subject: [PATCH] add wecima --- src/providers/all.ts | 2 + src/providers/sources/wecima.ts | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/providers/sources/wecima.ts diff --git a/src/providers/all.ts b/src/providers/all.ts index 4001735..dbf5ccb 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -95,6 +95,7 @@ import { uiraliveScraper } from './sources/uiralive'; import { vidapiClickScraper } from './sources/vidapiclick'; import { warezcdnScraper } from './sources/warezcdn'; import { webtorScraper } from './sources/webtor'; +import { wecimaScraper } from './sources/wecima'; import { xprimeScraper } from './sources/xprime'; export function gatherAllSources(): Array { @@ -128,6 +129,7 @@ export function gatherAllSources(): Array { ConsumetScraper, hianimeScraper, oneServerScraper, + wecimaScraper, ]; } diff --git a/src/providers/sources/wecima.ts b/src/providers/sources/wecima.ts new file mode 100644 index 0000000..d4bdb76 --- /dev/null +++ b/src/providers/sources/wecima.ts @@ -0,0 +1,101 @@ +import { load } from 'cheerio'; + +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +const baseUrl = 'https://wecima.tube'; + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const searchPage = await ctx.proxiedFetcher(`/search/${encodeURIComponent(ctx.media.title)}/`, { + baseUrl, + }); + + const search$ = load(searchPage); + const firstResult = search$('.Grid--WecimaPosts .GridItem a').first(); + if (!firstResult.length) throw new NotFoundError('No results found'); + + const contentUrl = firstResult.attr('href'); + if (!contentUrl) throw new NotFoundError('No content URL found'); + ctx.progress(30); + + const contentPage = await ctx.proxiedFetcher(contentUrl, { baseUrl }); + const content$ = load(contentPage); + + let embedUrl: string | undefined; + + if (ctx.media.type === 'movie') { + embedUrl = content$('meta[itemprop="embedURL"]').attr('content'); + } else { + const seasonLinks = content$('.List--Seasons--Episodes a'); + let seasonUrl: string | undefined; + + for (const element of seasonLinks) { + const text = content$(element).text().trim(); + if (text.includes(`موسم ${ctx.media.season}`)) { + seasonUrl = content$(element).attr('href'); + break; + } + } + + if (!seasonUrl) throw new NotFoundError(`Season ${ctx.media.season} not found`); + + const seasonPage = await ctx.proxiedFetcher(seasonUrl, { baseUrl }); + const season$ = load(seasonPage); + + const episodeLinks = season$('.Episodes--Seasons--Episodes a'); + for (const element of episodeLinks) { + const epTitle = season$(element).find('episodetitle').text().trim(); + if (epTitle === `الحلقة ${ctx.media.episode}`) { + const episodeUrl = season$(element).attr('href'); + if (episodeUrl) { + const episodePage = await ctx.proxiedFetcher(episodeUrl, { baseUrl }); + const episode$ = load(episodePage); + embedUrl = episode$('meta[itemprop="embedURL"]').attr('content'); + } + break; + } + } + } + + if (!embedUrl) throw new NotFoundError('No embed URL found'); + ctx.progress(60); + + // Get the final video URL + const embedPage = await ctx.proxiedFetcher(embedUrl); + const embed$ = load(embedPage); + const videoSource = embed$('source[type="video/mp4"]').attr('src'); + + if (!videoSource) throw new NotFoundError('No video source found'); + ctx.progress(90); + + return { + embeds: [], + stream: [ + { + id: 'primary', + type: 'file', + flags: [], + headers: { + referer: baseUrl, + }, + qualities: { + unknown: { + type: 'mp4', + url: videoSource, + }, + }, + captions: [], + }, + ], + }; +} + +export const wecimaScraper = makeSourcerer({ + id: 'wecima', + name: 'Wecima (Arabic)', + rank: 100, + flags: [], + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +});