diff --git a/src/providers/all.ts b/src/providers/all.ts index a81dfaf..af58f42 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -53,6 +53,7 @@ import { VidsrcsuServer8Scraper, VidsrcsuServer9Scraper, } from './embeds/vidsrcsu'; +import { vidzeeServer1Embed, vidzeeServer2Embed } from './embeds/vidzee'; import { viperScraper } from './embeds/viper'; import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls'; import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4'; @@ -73,6 +74,7 @@ import { soaperTvScraper } from './sources/soapertv'; import { streamboxScraper } from './sources/streambox'; import { vidapiClickScraper } from './sources/vidapiclick'; import { vidsrccxScraper } from './sources/vidsrccx'; +import { vidzeeScraper } from './sources/vidzee'; import { warezcdnScraper } from './sources/warezcdn'; import { wecimaScraper } from './sources/wecima'; @@ -110,6 +112,7 @@ export function gatherAllSources(): Array { pirxcyScraper, vidsrcvipScraper, vidsrccxScraper, + vidzeeScraper, ]; } @@ -159,5 +162,7 @@ export function gatherAllEmbeds(): Array { vidsrcNovaEmbed, vidsrcCometEmbed, vidsrcPulsarEmbed, + vidzeeServer1Embed, + vidzeeServer2Embed, ]; } diff --git a/src/providers/embeds/vidzee.ts b/src/providers/embeds/vidzee.ts new file mode 100644 index 0000000..a3a322a --- /dev/null +++ b/src/providers/embeds/vidzee.ts @@ -0,0 +1,188 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { EmbedOutput, makeEmbed } from '@/providers/base'; +import { NotFoundError } from '@/utils/errors'; +import { createM3U8ProxyUrl } from '@/utils/proxy'; + +const BaseUrl = 'https://player.vidzee.wtf/api/server'; + +const languageMap: Record = { + 'chinese - hong kong': 'zh', + 'chinese - traditional': 'zh', + czech: 'cs', + danish: 'da', + dutch: 'nl', + english: 'en', + 'english - sdh': 'en', + finnish: 'fi', + french: 'fr', + german: 'de', + greek: 'el', + hungarian: 'hu', + italian: 'it', + korean: 'ko', + norwegian: 'no', + polish: 'pl', + portuguese: 'pt', + 'portuguese - brazilian': 'pt', + romanian: 'ro', + 'spanish - european': 'es', + 'spanish - latin american': 'es', + spanish: 'es', + swedish: 'sv', + turkish: 'tr', + اَلْعَرَبِيَّةُ: 'ar', + বাংলা: 'bn', + filipino: 'tl', + indonesia: 'id', + اردو: 'ur', + English: 'en', + Arabic: 'ar', + Bosnian: 'bs', + Bulgarian: 'bg', + Croatian: 'hr', + Czech: 'cs', + Danish: 'da', + Dutch: 'nl', + Estonian: 'et', + Finnish: 'fi', + French: 'fr', + German: 'de', + Greek: 'el', + Hebrew: 'he', + Hungarian: 'hu', + Indonesian: 'id', + Italian: 'it', + Norwegian: 'no', + Persian: 'fa', + Polish: 'pl', + Portuguese: 'pt', + 'Protuguese (BR)': 'pt-br', + Romanian: 'ro', + Russian: 'ru', + russian: 'ru', + Serbian: 'sr', + Slovenian: 'sl', + Spanish: 'es', + Swedish: 'sv', + Thai: 'th', + Turkish: 'tr', +}; + +export const vidzeeServer1Embed = makeEmbed({ + id: 'vidzee-server1', + name: 'Server 1', + rank: 34, + async scrape(ctx): Promise { + const query = JSON.parse(ctx.url); + const params = new URLSearchParams({ + id: query.tmdbId, + sr: '1', + }); + + if (query.type === 'show') { + params.append('ss', query.season.toString()); + params.append('ep', query.episode.toString()); + } + + const data = await ctx.fetcher(`${BaseUrl}?${params.toString()}`); + if (!data) throw new NotFoundError('No response received'); + if (!data.url || !Array.isArray(data.url) || data.url.length === 0) { + throw new NotFoundError('No stream URL found in response'); + } + + const streamData = data.url[0]; + const streamUrl = streamData.link; + const headers = data.headers || {}; + + if (headers.Referer && headers.Referer.includes('vidzee')) { + headers.Origin = 'https://player.vidzee.wtf'; + } + + const captions = + data.tracks?.map((track: { lang: string; url: string }, i: number) => { + const languageBase = track.lang.toLowerCase(); + return { + id: i, + type: 'vtt', + url: track.url, + language: languageMap[languageBase] || 'unknown', + }; + }) || []; + + ctx.progress(90); + + const finalUrl = Object.keys(headers).length > 0 ? createM3U8ProxyUrl(streamUrl, headers) : streamUrl; + + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist: finalUrl, + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + }; + }, +}); + +export const vidzeeServer2Embed = makeEmbed({ + id: 'vidzee-server2', + name: 'Server 2', + rank: 33, + async scrape(ctx): Promise { + const query = JSON.parse(ctx.url); + const params = new URLSearchParams({ + id: query.tmdbId, + sr: '2', + }); + + if (query.type === 'show') { + params.append('ss', query.season.toString()); + params.append('ep', query.episode.toString()); + } + + const data = await ctx.fetcher(`${BaseUrl}?${params.toString()}`); + if (!data) throw new NotFoundError('No response received'); + if (!data.url || !Array.isArray(data.url) || data.url.length === 0) { + throw new NotFoundError('No stream URL found in response'); + } + + const streamData = data.url[0]; + const streamUrl = streamData.link; + const headers = data.headers || {}; + + // Add Origin header if Referer contains vidzee + if (headers.Referer && headers.Referer.includes('vidzee')) { + headers.Origin = 'https://player.vidzee.wtf/'; + } + + const captions = + data.tracks?.map((track: { lang: string; url: string }, i: number) => { + const languageBase = track.lang.toLowerCase(); + return { + id: i, + type: 'vtt', + url: track.url, + language: languageMap[languageBase] || 'unknown', + }; + }) || []; + + ctx.progress(90); + + const finalUrl = Object.keys(headers).length > 0 ? createM3U8ProxyUrl(streamUrl, headers) : streamUrl; + + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist: finalUrl, + flags: [flags.CORS_ALLOWED], + captions, + }, + ], + }; + }, +}); diff --git a/src/providers/sources/vidzee.ts b/src/providers/sources/vidzee.ts new file mode 100644 index 0000000..2b8cdf2 --- /dev/null +++ b/src/providers/sources/vidzee.ts @@ -0,0 +1,40 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { SourcererOutput, makeSourcerer } from '@/providers/base'; +import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context'; + +async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise { + const query = { + type: ctx.media.type, + title: ctx.media.title, + tmdbId: ctx.media.tmdbId, + imdbId: ctx.media.imdbId, + ...(ctx.media.type === 'show' && { + season: ctx.media.season.number, + episode: ctx.media.episode.number, + }), + releaseYear: ctx.media.releaseYear, + }; + + const embeds = [ + { + embedId: 'vidzee-server1', + url: JSON.stringify(query), + }, + { + embedId: 'vidzee-server2', + url: JSON.stringify(query), + }, + ]; + + return { embeds }; +} + +export const vidzeeScraper = makeSourcerer({ + id: 'vidzee', + name: 'Vidzee', + rank: 164, + disabled: false, + flags: [flags.CORS_ALLOWED], + scrapeMovie: comboScraper, + scrapeShow: comboScraper, +});