diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index ad9358c..dc8a053 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -1,3 +1,4 @@ +import { load } from 'cheerio'; import { unpack } from 'unpacker'; import { flags } from '@/entrypoint/utils/targets'; @@ -19,7 +20,8 @@ export const fileMoonScraper = makeEmbed({ referer: ctx.url, }, }); - const evalCode = embedRes.match(evalCodeRegex); + const embedHtml = load(embedRes); + const evalCode = embedHtml('script').text().match(evalCodeRegex); if (!evalCode) throw new Error('Failed to find eval code'); const unpacked = unpack(evalCode[1]); const file = fileRegex.exec(unpacked); diff --git a/src/providers/embeds/mixdrop.ts b/src/providers/embeds/mixdrop.ts index 71fd0ac..9a9ee60 100644 --- a/src/providers/embeds/mixdrop.ts +++ b/src/providers/embeds/mixdrop.ts @@ -2,6 +2,7 @@ import * as unpacker from 'unpacker'; import { makeEmbed } from '@/providers/base'; +const mixdropBase = 'https://mixdrop.ag'; const packedRegex = /(eval\(function\(p,a,c,k,e,d\){.*{}\)\))/; const linkRegex = /MDCore\.wurl="(.*?)";/; @@ -12,7 +13,16 @@ export const mixdropScraper = makeEmbed({ async scrape(ctx) { // Example url: https://mixdrop.co/e/pkwrgp0pizgod0 // Example url: https://mixdrop.vc/e/pkwrgp0pizgod0 - const streamRes = await ctx.proxiedFetcher(ctx.url); + let embedUrl = ctx.url; + if (ctx.url.includes('primewire')) embedUrl = (await ctx.fetcher.full(ctx.url)).finalUrl; + const embedId = new URL(embedUrl).pathname.split('/')[2]; + // constructing the url because many times mixdrop.co is returned which does not work + // this also handels the case where preview page urls are returned + // Example: https://mixdrop.vc/f/pkwrgp0pizgod0 + // these don't have the packed code + const streamRes = await ctx.proxiedFetcher(`/e/${embedId}`, { + baseUrl: mixdropBase, + }); const packed = streamRes.match(packedRegex); // MixDrop uses a queue system for embeds @@ -45,7 +55,7 @@ export const mixdropScraper = makeEmbed({ url: url.startsWith('http') ? url : `https:${url}`, // URLs don't always start with the protocol headers: { // MixDrop requires this header on all streams - Referer: 'https://mixdrop.co/', + Referer: mixdropBase, }, }, }, diff --git a/src/providers/embeds/upcloud.ts b/src/providers/embeds/upcloud.ts index 663bb3d..63d8407 100644 --- a/src/providers/embeds/upcloud.ts +++ b/src/providers/embeds/upcloud.ts @@ -60,6 +60,7 @@ export const upcloudScraper = makeEmbed({ id: 'upcloud', name: 'UpCloud', rank: 200, + disabled: true, async scrape(ctx) { // Example url: https://dokicloud.one/embed-4/{id}?z= const parsedUrl = new URL(ctx.url.replace('embed-5', 'embed-4')); diff --git a/src/providers/embeds/vidcloud.ts b/src/providers/embeds/vidcloud.ts index 2aca309..d5c50d4 100644 --- a/src/providers/embeds/vidcloud.ts +++ b/src/providers/embeds/vidcloud.ts @@ -6,6 +6,7 @@ export const vidCloudScraper = makeEmbed({ id: 'vidcloud', name: 'VidCloud', rank: 201, + disabled: true, async scrape(ctx) { // Both vidcloud and upcloud have the same embed domain (rabbitstream.net) const result = await upcloudScraper.scrape(ctx); diff --git a/src/providers/embeds/vidplay/index.ts b/src/providers/embeds/vidplay/index.ts index 045e19e..fb5b7c2 100644 --- a/src/providers/embeds/vidplay/index.ts +++ b/src/providers/embeds/vidplay/index.ts @@ -3,7 +3,7 @@ import { makeEmbed } from '@/providers/base'; import { Caption, getCaptionTypeFromUrl, labelToLanguageCode } from '@/providers/captions'; import { getFileUrl } from './common'; -import { SubtitleResult, VidplaySourceResponse } from './types'; +import { SubtitleResult, ThumbnailTrack, VidplaySourceResponse } from './types'; export const vidplayScraper = makeEmbed({ id: 'vidplay', @@ -18,6 +18,15 @@ export const vidplayScraper = makeEmbed({ }); if (typeof fileUrlRes.result === 'number') throw new Error('File not found'); const source = fileUrlRes.result.sources[0].file; + const thumbnailSource = fileUrlRes.result.tracks.find((track) => track.kind === 'thumbnails'); + + let thumbnailTrack: ThumbnailTrack | undefined; + if (thumbnailSource) { + thumbnailTrack = { + type: 'vtt', + url: thumbnailSource.file, + }; + } const url = new URL(ctx.url); const subtitlesLink = url.searchParams.get('sub.info'); @@ -47,6 +56,7 @@ export const vidplayScraper = makeEmbed({ playlist: source, flags: [flags.CORS_ALLOWED], captions, + thumbnailTrack, }, ], }; diff --git a/src/providers/embeds/vidplay/types.ts b/src/providers/embeds/vidplay/types.ts index 29cde1d..8810647 100644 --- a/src/providers/embeds/vidplay/types.ts +++ b/src/providers/embeds/vidplay/types.ts @@ -3,10 +3,10 @@ export type VidplaySourceResponse = { | { sources: { file: string; - tracks: { - file: string; - kind: string; - }[]; + }[]; + tracks: { + file: string; + kind: string; }[]; } | number; @@ -17,3 +17,8 @@ export type SubtitleResult = { label: string; kind: string; }[]; + +export type ThumbnailTrack = { + type: 'vtt'; + url: string; +}; diff --git a/src/providers/sources/gomovies/index.ts b/src/providers/sources/gomovies/index.ts index b57e6be..419efba 100644 --- a/src/providers/sources/gomovies/index.ts +++ b/src/providers/sources/gomovies/index.ts @@ -2,7 +2,12 @@ import { load } from 'cheerio'; import { flags } from '@/entrypoint/utils/targets'; import { makeSourcerer } from '@/providers/base'; +import { doodScraper } from '@/providers/embeds/dood'; +import { mixdropScraper } from '@/providers/embeds/mixdrop'; import { upcloudScraper } from '@/providers/embeds/upcloud'; +import { upstreamScraper } from '@/providers/embeds/upstream'; +import { vidCloudScraper } from '@/providers/embeds/vidcloud'; +import { voeScraper } from '@/providers/embeds/voe'; import { NotFoundError } from '@/utils/errors'; import { getSource } from './source'; @@ -13,12 +18,11 @@ export const goMoviesScraper = makeSourcerer({ id: 'gomovies', name: 'GOmovies', rank: 60, - flags: [flags.CORS_ALLOWED], disabled: true, + flags: [flags.CORS_ALLOWED], async scrapeShow(ctx) { - const search = await ctx.proxiedFetcher(`/ajax/search`, { - method: 'POST', - body: new URLSearchParams({ keyword: ctx.media.title }), + const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, { + method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest', }, @@ -26,12 +30,12 @@ export const goMoviesScraper = makeSourcerer({ }); const searchPage = load(search); - const mediaElements = searchPage('a.nav-item'); + const mediaElements = searchPage('div.film-detail'); const mediaData = mediaElements.toArray().map((movieEl) => { - const name = searchPage(movieEl).find('h3.film-name')?.text(); - const year = searchPage(movieEl).find('div.film-infor span:first-of-type')?.text(); - const path = searchPage(movieEl).attr('href'); + const name = searchPage(movieEl).find('h2.film-name a')?.text(); + const year = searchPage(movieEl).find('span.fdi-item:first')?.text(); + const path = searchPage(movieEl).find('h2.film-name a').attr('href'); return { name, year, path }; }); @@ -89,21 +93,56 @@ export const goMoviesScraper = makeSourcerer({ }, }); - const upcloudSource = await getSource(ctx, sources); + const upcloudSource = await getSource(ctx, sources, 'upcloud'); + const vidcloudSource = await getSource(ctx, sources, 'vidcloud'); + const voeSource = await getSource(ctx, sources, 'voe'); + const doodSource = await getSource(ctx, sources, 'doodstream'); + const upstreamSource = await getSource(ctx, sources, 'upstream'); + const mixdropSource = await getSource(ctx, sources, 'mixdrop'); + + const embeds = [ + { + embedId: upcloudScraper.id, + url: upcloudSource?.link, + }, + { + embedId: vidCloudScraper.id, + url: vidcloudSource?.link, + }, + { + embedId: voeScraper.id, + url: voeSource?.link, + }, + { + embedId: doodScraper.id, + url: doodSource?.link, + }, + { + embedId: upstreamScraper.id, + url: upstreamSource?.link, + }, + { + embedId: mixdropScraper.id, + url: mixdropSource?.link, + }, + ]; + + const filteredEmbeds = embeds + .filter((embed) => embed.url) + .map((embed) => ({ + embedId: embed.embedId, + url: embed.url as string, + })); + + if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.'); return { - embeds: [ - { - embedId: upcloudScraper.id, - url: upcloudSource.link, - }, - ], + embeds: filteredEmbeds, }; }, async scrapeMovie(ctx) { - const search = await ctx.proxiedFetcher(`ajax/search`, { - method: 'POST', - body: new URLSearchParams({ keyword: ctx.media.title }), + const search = await ctx.proxiedFetcher(`/search/${ctx.media.title.replaceAll(/[^a-z0-9A-Z]/g, '-')}`, { + method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest', }, @@ -111,12 +150,12 @@ export const goMoviesScraper = makeSourcerer({ }); const searchPage = load(search); - const mediaElements = searchPage('a.nav-item'); + const mediaElements = searchPage('div.film-detail'); const mediaData = mediaElements.toArray().map((movieEl) => { - const name = searchPage(movieEl).find('h3.film-name')?.text(); - const year = searchPage(movieEl).find('div.film-infor span:first-of-type')?.text(); - const path = searchPage(movieEl).attr('href'); + const name = searchPage(movieEl).find('h2.film-name a')?.text(); + const year = searchPage(movieEl).find('span.fdi-item:first')?.text(); + const path = searchPage(movieEl).find('h2.film-name a').attr('href'); return { name, year, path }; }); @@ -136,15 +175,51 @@ export const goMoviesScraper = makeSourcerer({ baseUrl: gomoviesBase, }); - const upcloudSource = await getSource(ctx, sources); + const upcloudSource = await getSource(ctx, sources, 'upcloud'); + const vidcloudSource = await getSource(ctx, sources, 'vidcloud'); + const voeSource = await getSource(ctx, sources, 'voe'); + const doodSource = await getSource(ctx, sources, 'doodstream'); + const upstreamSource = await getSource(ctx, sources, 'upstream'); + const mixdropSource = await getSource(ctx, sources, 'mixdrop'); + + const embeds = [ + { + embedId: upcloudScraper.id, + url: upcloudSource?.link, + }, + { + embedId: vidCloudScraper.id, + url: vidcloudSource?.link, + }, + { + embedId: voeScraper.id, + url: voeSource?.link, + }, + { + embedId: doodScraper.id, + url: doodSource?.link, + }, + { + embedId: upstreamScraper.id, + url: upstreamSource?.link, + }, + { + embedId: mixdropScraper.id, + url: mixdropSource?.link, + }, + ]; + + const filteredEmbeds = embeds + .filter((embed) => embed.url) + .map((embed) => ({ + embedId: embed.embedId, + url: embed.url as string, + })); + + if (filteredEmbeds.length === 0) throw new Error('No valid embeds found.'); return { - embeds: [ - { - embedId: upcloudScraper.id, - url: upcloudSource.link, - }, - ], + embeds: filteredEmbeds, }; }, }); diff --git a/src/providers/sources/gomovies/source.ts b/src/providers/sources/gomovies/source.ts index 279a35c..4ef93bc 100644 --- a/src/providers/sources/gomovies/source.ts +++ b/src/providers/sources/gomovies/source.ts @@ -1,31 +1,30 @@ import { load } from 'cheerio'; import { ScrapeContext } from '@/utils/context'; -import { NotFoundError } from '@/utils/errors'; import { gomoviesBase } from '.'; -export async function getSource(ctx: ScrapeContext, sources: any) { - const upcloud = load(sources)('a[title*="upcloud" i]'); +export async function getSource(ctx: ScrapeContext, sources: any, title: string) { + const source = load(sources)(`a[title*=${title} i]`); - const upcloudDataId = upcloud?.attr('data-id') ?? upcloud?.attr('data-linkid'); + const sourceDataId = source?.attr('data-id') ?? source?.attr('data-linkid'); - if (!upcloudDataId) throw new NotFoundError('Upcloud source not available'); + if (!sourceDataId) return undefined; - const upcloudSource = await ctx.proxiedFetcher<{ + const sourceData = await ctx.proxiedFetcher<{ type: 'iframe' | string; link: string; sources: []; title: string; tracks: []; - }>(`/ajax/sources/${upcloudDataId}`, { + }>(`/ajax/sources/${sourceDataId}`, { headers: { 'X-Requested-With': 'XMLHttpRequest', }, baseUrl: gomoviesBase, }); - if (!upcloudSource.link || upcloudSource.type !== 'iframe') throw new NotFoundError('No upcloud stream found'); + if (!sourceData.link || sourceData.type !== 'iframe') return undefined; - return upcloudSource; + return sourceData; }