diff --git a/src/providers/all.ts b/src/providers/all.ts index efcec9b..d9e544b 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -61,6 +61,7 @@ import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls'; import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4'; import { warezPlayerScraper } from './embeds/warezcdn/warezplayer'; import { webtor1080Scraper, webtor480Scraper, webtor4kScraper, webtor720Scraper } from './embeds/webtor'; +import { EightStreamScraper } from './sources/8stream'; import { coitusScraper } from './sources/coitus'; import { embedsuScraper } from './sources/embedsu'; import { FedAPIScraper } from './sources/fedapi'; @@ -104,6 +105,7 @@ export function gatherAllSources(): Array { streamboxScraper, nunflixScraper, riveScraper, + EightStreamScraper, ]; } diff --git a/src/providers/sources/8stream/8Stream.ts b/src/providers/sources/8stream/8Stream.ts new file mode 100644 index 0000000..a47a33a --- /dev/null +++ b/src/providers/sources/8stream/8Stream.ts @@ -0,0 +1,85 @@ +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import getInfo from './getInfo'; +import getStream from './getStream'; + +export async function getMovie(ctx: ShowScrapeContext | MovieScrapeContext, id: string, lang: string = 'English') { + try { + const mediaInfo = await getInfo(ctx, id); + if (mediaInfo?.success) { + const playlist = mediaInfo?.data?.playlist; + if (!playlist || !Array.isArray(playlist)) { + throw new NotFoundError('Playlist not found or invalid'); + } + let file = playlist.find((item: any) => item?.title === lang); + if (!file) { + file = playlist?.[0]; + } + if (!file) { + throw new NotFoundError('No file found'); + } + const availableLang = playlist.map((item: any) => item?.title); + const key = mediaInfo?.data?.key; + ctx.progress(70); + const streamUrl = await getStream(ctx, file?.file, key); + if (streamUrl?.success) { + return { success: true, data: streamUrl?.data, availableLang }; + } + throw new NotFoundError('No stream url found'); + } + throw new NotFoundError('No media info found'); + } catch (error) { + if (error instanceof NotFoundError) throw error; + throw new NotFoundError('Failed to fetch movie data'); + } +} + +export async function getTV( + ctx: ShowScrapeContext | MovieScrapeContext, + id: string, + season: number, + episode: number, + lang: string, +) { + try { + const mediaInfo = await getInfo(ctx, id); + if (!mediaInfo?.success) { + throw new NotFoundError('No media info found'); + } + const playlist = mediaInfo?.data?.playlist; + const getSeason = playlist.find((item: any) => item?.id === season.toString()); + if (!getSeason) { + throw new NotFoundError('No season found'); + } + const getEpisode = getSeason?.folder.find((item: any) => item?.episode === episode.toString()); + if (!getEpisode) { + throw new NotFoundError('No episode found'); + } + let file = getEpisode?.folder.find((item: any) => item?.title === lang); + if (!file) { + file = getEpisode?.folder?.[0]; + } + if (!file) { + throw new NotFoundError('No file found'); + } + const availableLang = getEpisode?.folder.map((item: any) => { + return item?.title; + }); + const filterLang = availableLang.filter((item: any) => item?.length > 0); + const key = mediaInfo?.data?.key; + ctx.progress(70); + const streamUrl = await getStream(ctx, file?.file, key); + if (streamUrl?.success) { + return { + success: true, + data: streamUrl?.data, + availableLang: filterLang, + }; + } + throw new NotFoundError('No stream url found'); + } catch (error) { + if (error instanceof NotFoundError) throw error; + throw new NotFoundError('Failed to fetch TV data'); + } +} diff --git a/src/providers/sources/8stream/getInfo.ts b/src/providers/sources/8stream/getInfo.ts new file mode 100644 index 0000000..08fb132 --- /dev/null +++ b/src/providers/sources/8stream/getInfo.ts @@ -0,0 +1,63 @@ +import * as cheerio from 'cheerio'; + +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +export default async function getStream(ctx: ShowScrapeContext | MovieScrapeContext, id: string): Promise { + try { + const baseUrl = 'https://ftmoh345xme.com'; + const headers = { + Origin: 'https://friness-cherlormur-i-275.site', + Referer: 'https://google.com/', + Dnt: '1', + }; + const url = `${baseUrl}/play/${id}`; + const result = await ctx.proxiedFetcher(url, { + headers: { + ...headers, + }, + method: 'GET', + }); + const $ = cheerio.load(result); + const script = $('script').last().html()!; + if (!script) { + throw new NotFoundError('Failed to extract script data'); + } + const content = script.match(/(\{[^;]+});/)?.[1] || script.match(/\((\{.*\})\)/)?.[1]; + if (!content) { + throw new NotFoundError('Media not found'); + } + const data = JSON.parse(content); + let file = data.file; + if (!file) { + throw new NotFoundError('File not found'); + } + if (file.startsWith('/')) { + file = baseUrl + file; + } + const key = data.key; + const headers2 = { + Origin: 'https://friness-cherlormur-i-275.site', + Referer: 'https://google.com/', + Dnt: '1', + 'X-Csrf-Token': key, + }; + const PlayListRes = await ctx.proxiedFetcher(file, { + headers: { + ...headers2, + }, + method: 'GET', + }); + const playlist = PlayListRes; + return { + success: true, + data: { + playlist, + key, + }, + }; + } catch (error) { + if (error instanceof NotFoundError) throw error; + throw new NotFoundError('Failed to fetch media info'); + } +} diff --git a/src/providers/sources/8stream/getStream.ts b/src/providers/sources/8stream/getStream.ts new file mode 100644 index 0000000..c5af1ef --- /dev/null +++ b/src/providers/sources/8stream/getStream.ts @@ -0,0 +1,36 @@ +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +export default async function getStream( + ctx: ShowScrapeContext | MovieScrapeContext, + file: string, + key: string, +): Promise { + const f = file as string; + const path = `${f.slice(1)}.txt`; + try { + const baseUrl = 'https://ftmoh345xme.com'; + const headers = { + Origin: 'https://friness-cherlormur-i-275.site', + Referer: 'https://google.com/', + Dnt: '1', + 'X-Csrf-Token': key, + }; + const url = `${baseUrl}/playlist/${path}`; + const result = await ctx.proxiedFetcher(url, { + headers: { + ...headers, + }, + method: 'GET', + }); + + return { + success: true, + data: { + link: result, + }, + }; + } catch (error) { + throw new NotFoundError('Failed to fetch stream data'); + } +} diff --git a/src/providers/sources/8stream/index.ts b/src/providers/sources/8stream/index.ts new file mode 100644 index 0000000..602a158 --- /dev/null +++ b/src/providers/sources/8stream/index.ts @@ -0,0 +1,77 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { getMovie, getTV } from './8Stream'; + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const query = { + title: ctx.media.title, + releaseYear: ctx.media.releaseYear, + tmdbId: ctx.media.tmdbId, + imdbId: ctx.media.imdbId, + type: ctx.media.type, + season: '', + episode: '', + }; + + if (ctx.media.type === 'show') { + query.season = ctx.media.season.number.toString(); + query.episode = ctx.media.episode.number.toString(); + } + + if (ctx.media.type === 'movie') { + ctx.progress(40); + const res = await getMovie(ctx, ctx.media.imdbId as string); + if (res?.success) { + ctx.progress(90); + return { + embeds: [], + stream: [ + { + id: 'primary', + captions: [], + playlist: res.data.link, + type: 'hls', + flags: [flags.CORS_ALLOWED], + }, + ], + }; + } + throw new NotFoundError('No providers available'); + } + + if (ctx.media.type === 'show') { + ctx.progress(40); + const lang = 'English'; + const res = await getTV(ctx, ctx.media.imdbId as string, ctx.media.season.number, ctx.media.episode.number, lang); + if (res?.success) { + ctx.progress(90); + return { + embeds: [], + stream: [ + { + id: 'primary', + captions: [], + playlist: res.data.link, + type: 'hls', + flags: [flags.CORS_ALLOWED], + }, + ], + }; + } + throw new NotFoundError('No providers available'); + } + throw new NotFoundError('No providers available'); +} + +export const EightStreamScraper = makeSourcerer({ + id: '8stream', + name: '8stream', + rank: 111, + flags: [], + disabled: false, + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +});