From 68a9c94a64b865829e4fda50547beb475bcfdcd3 Mon Sep 17 00:00:00 2001 From: himanshu8443 Date: Sat, 21 Jun 2025 18:26:43 +0530 Subject: [PATCH] add provider 4khdhub --- dist/4khdhub/catalog.js | 22 ++++ dist/4khdhub/meta.js | 97 ++++++++++++++++++ dist/4khdhub/posts.js | 68 +++++++++++++ dist/4khdhub/stream.js | 164 ++++++++++++++++++++++++++++++ manifest.json | 8 ++ providers/4khdhub/catalog.ts | 20 ++++ providers/4khdhub/meta.ts | 92 +++++++++++++++++ providers/4khdhub/posts.ts | 81 +++++++++++++++ providers/4khdhub/stream.ts | 188 +++++++++++++++++++++++++++++++++++ 9 files changed, 740 insertions(+) create mode 100644 dist/4khdhub/catalog.js create mode 100644 dist/4khdhub/meta.js create mode 100644 dist/4khdhub/posts.js create mode 100644 dist/4khdhub/stream.js create mode 100644 providers/4khdhub/catalog.ts create mode 100644 providers/4khdhub/meta.ts create mode 100644 providers/4khdhub/posts.ts create mode 100644 providers/4khdhub/stream.ts diff --git a/dist/4khdhub/catalog.js b/dist/4khdhub/catalog.js new file mode 100644 index 0000000..44e9eae --- /dev/null +++ b/dist/4khdhub/catalog.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.genres = exports.catalog = void 0; +exports.catalog = [ + { + title: "Popular Movies", + filter: "/category/new-movies-10810.html", + }, + { + title: "Latest TV Shows", + filter: "/category/new-series-10811.html", + }, + { + title: "Anime", + filter: "/category/anime-10812.html", + }, + { + title: "4K HDR", + filter: "/category/4k-hdr-10776.html", + }, +]; +exports.genres = []; diff --git a/dist/4khdhub/meta.js b/dist/4khdhub/meta.js new file mode 100644 index 0000000..f0ac9e0 --- /dev/null +++ b/dist/4khdhub/meta.js @@ -0,0 +1,97 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getMeta = void 0; +const getMeta = function (_a) { + return __awaiter(this, arguments, void 0, function* ({ link, providerContext, }) { + try { + const { axios, cheerio, getBaseUrl } = providerContext; + const baseUrl = yield getBaseUrl("4khdhub"); + const url = `${baseUrl}${link}`; + const res = yield axios.get(url); + const data = res.data; + const $ = cheerio.load(data); + const type = $(".season-content").length > 0 ? "series" : "movie"; + const imdbId = ""; + const title = $(".page-title").text() || ""; + const image = $(".poster-image").find("img").attr("src") || ""; + const synopsis = $(".content-section").find("p").first().text().trim() || ""; + // Links + const links = []; + if (type === "series") { + $(".season-item").map((i, element) => { + const title = $(element).find(".episode-title").text(); + let directLinks = []; + $(element) + .find(".episode-download-item") + .map((i, element) => { + const title = $(element) + .find(".episode-file-info") + .text() + .trim() + .replace("\n", " "); + const link = $(element) + .find(".episode-links") + .find("a:contains('HubDrive')") + .attr("href"); + console.log("title⭐", title, "link", link); + if (title && link) { + directLinks.push({ title, link }); + } + }); + if (title && directLinks.length > 0) { + links.push({ + title, + directLinks: directLinks, + }); + } + }); + } + else { + $(".download-item").map((i, element) => { + const title = $(element) + .find(".flex-1.text-left.font-semibold") + .text() + .trim(); + const link = $(element) + .find(".grid.grid-cols-2.gap-2") + .find("a:contains('HubDrive')") + .attr("href"); + // console.log("title⭐", title, "link", link); + if (title && link) { + links.push({ title, directLinks: [{ title, link }] }); + } + }); + } + // console.log('multi meta', links); + return { + title, + synopsis, + image, + imdbId, + type, + linkList: links, + }; + } + catch (err) { + console.error(err); + return { + title: "", + synopsis: "", + image: "", + imdbId: "", + type: "movie", + linkList: [], + }; + } + }); +}; +exports.getMeta = getMeta; diff --git a/dist/4khdhub/posts.js b/dist/4khdhub/posts.js new file mode 100644 index 0000000..b02b2eb --- /dev/null +++ b/dist/4khdhub/posts.js @@ -0,0 +1,68 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSearchPosts = exports.getPosts = void 0; +const getPosts = function (_a) { + return __awaiter(this, arguments, void 0, function* ({ filter, page, signal, providerContext, }) { + const { getBaseUrl, cheerio } = providerContext; + const baseUrl = yield getBaseUrl("4khdhub"); + const url = `${baseUrl + filter}/page/${page}.html`; + console.log("4khdhubGetPosts url", url); + return posts({ url, signal, cheerio }); + }); +}; +exports.getPosts = getPosts; +const getSearchPosts = function (_a) { + return __awaiter(this, arguments, void 0, function* ({ searchQuery, page, signal, providerContext, }) { + const { getBaseUrl, cheerio } = providerContext; + const baseUrl = yield getBaseUrl("4khdhub"); + const url = `${baseUrl}/page/${page}.html?s=hin${searchQuery}`; + return posts({ url, signal, cheerio }); + }); +}; +exports.getSearchPosts = getSearchPosts; +function posts(_a) { + return __awaiter(this, arguments, void 0, function* ({ url, signal, cheerio, }) { + try { + const res = yield fetch(url, { signal }); + const data = yield res.text(); + const $ = cheerio.load(data); + const catalog = []; + $(".card-grid") + .children() + .map((i, element) => { + const title = $(element).find(".movie-card-title").text(); + const link = $(element).attr("href"); + const image = $(element).find("img").attr("src"); + // console.log( + // "4khdhubGetPosts title", + // title, + // "link", + // link, + // "image", + // image + // ); + if (title && link && image) { + catalog.push({ + title: title, + link: link, + image: image, + }); + } + }); + return catalog; + } + catch (err) { + console.error("4khdhubGetPosts error ", err); + return []; + } + }); +} diff --git a/dist/4khdhub/stream.js b/dist/4khdhub/stream.js new file mode 100644 index 0000000..3d9790b --- /dev/null +++ b/dist/4khdhub/stream.js @@ -0,0 +1,164 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getStream = getStream; +exports.getRedirectLinks = getRedirectLinks; +exports.decodeString = decodeString; +function getStream(_a) { + return __awaiter(this, arguments, void 0, function* ({ link, signal, providerContext, }) { + var _b, _c, _d, _e; + const { axios, cheerio, extractors, commonHeaders: headers, } = providerContext; + const { hubcloudExtracter } = extractors; + let hubdriveLink = ""; + if (link.includes("hubdrive")) { + const hubdriveRes = yield axios.get(link, { headers, signal }); + const hubdriveText = hubdriveRes.data; + const $ = cheerio.load(hubdriveText); + hubdriveLink = + $(".btn.btn-primary.btn-user.btn-success1.m-1").attr("href") || link; + } + else { + const res = yield axios.get(link, { headers, signal }); + const text = res.data; + const encryptedString = (_d = (_c = (_b = text.split("s('o','")) === null || _b === void 0 ? void 0 : _b[1]) === null || _c === void 0 ? void 0 : _c.split("',180")) === null || _d === void 0 ? void 0 : _d[0]; + const decodedString = decodeString(encryptedString); + link = atob(decodedString === null || decodedString === void 0 ? void 0 : decodedString.o); + const redirectLink = yield getRedirectLinks(link, signal, headers); + const redirectLinkRes = yield axios.get(redirectLink, { headers, signal }); + const redirectLinkText = redirectLinkRes.data; + const $ = cheerio.load(redirectLinkText); + hubdriveLink = + $('h3:contains("1080p")').find("a").attr("href") || + redirectLinkText.match(/href="(https:\/\/hubcloud\.[^\/]+\/drive\/[^"]+)"/)[1]; + if (hubdriveLink.includes("hubdrive")) { + const hubdriveRes = yield axios.get(hubdriveLink, { headers, signal }); + const hubdriveText = hubdriveRes.data; + const $$ = cheerio.load(hubdriveText); + hubdriveLink = + $$(".btn.btn-primary.btn-user.btn-success1.m-1").attr("href") || + hubdriveLink; + } + } + const hubdriveLinkRes = yield axios.get(hubdriveLink, { headers, signal }); + const hubcloudText = hubdriveLinkRes.data; + const hubcloudLink = ((_e = hubcloudText.match(//i)) === null || _e === void 0 ? void 0 : _e[1]) || hubdriveLink; + try { + return yield hubcloudExtracter(hubcloudLink, signal); + } + catch (error) { + console.log("hd hub 4 getStream error: ", error); + return []; + } + }); +} +const encode = function (value) { + return btoa(value.toString()); +}; +const decode = function (value) { + if (value === undefined) { + return ""; + } + return atob(value.toString()); +}; +const pen = function (value) { + return value.replace(/[a-zA-Z]/g, function (_0x1a470e) { + return String.fromCharCode((_0x1a470e <= "Z" ? 90 : 122) >= + (_0x1a470e = _0x1a470e.charCodeAt(0) + 13) + ? _0x1a470e + : _0x1a470e - 26); + }); +}; +const abortableTimeout = (ms, { signal } = {}) => { + return new Promise((resolve, reject) => { + if (signal && signal.aborted) { + return reject(new Error("Aborted")); + } + const timer = setTimeout(resolve, ms); + if (signal) { + signal.addEventListener("abort", () => { + clearTimeout(timer); + reject(new Error("Aborted")); + }); + } + }); +}; +function getRedirectLinks(link, signal, headers) { + return __awaiter(this, void 0, void 0, function* () { + try { + const res = yield fetch(link, { headers, signal }); + const resText = yield res.text(); + var regex = /ck\('_wp_http_\d+','([^']+)'/g; + var combinedString = ""; + var match; + while ((match = regex.exec(resText)) !== null) { + // console.log(match[1]); + combinedString += match[1]; + } + // console.log(decode(combinedString)); + const decodedString = decode(pen(decode(decode(combinedString)))); + // console.log(decodedString); + const data = JSON.parse(decodedString); + console.log(data); + const token = encode(data === null || data === void 0 ? void 0 : data.data); + const blogLink = (data === null || data === void 0 ? void 0 : data.wp_http1) + "?re=" + token; + // abort timeout on signal + let wait = abortableTimeout((Number(data === null || data === void 0 ? void 0 : data.total_time) + 3) * 1000, { + signal, + }); + yield wait; + console.log("blogLink", blogLink); + let vcloudLink = "Invalid Request"; + while (vcloudLink.includes("Invalid Request")) { + const blogRes = yield fetch(blogLink, { headers, signal }); + const blogResText = (yield blogRes.text()); + if (blogResText.includes("Invalid Request")) { + console.log(blogResText); + } + else { + vcloudLink = blogResText.match(/var reurl = "([^"]+)"/) || ""; + break; + } + } + // console.log('vcloudLink', vcloudLink?.[1]); + return blogLink || link; + } + catch (err) { + console.log("Error in getRedirectLinks", err); + return link; + } + }); +} +function rot13(str) { + return str.replace(/[a-zA-Z]/g, function (char) { + const charCode = char.charCodeAt(0); + const isUpperCase = char <= "Z"; + const baseCharCode = isUpperCase ? 65 : 97; + return String.fromCharCode(((charCode - baseCharCode + 13) % 26) + baseCharCode); + }); +} +function decodeString(encryptedString) { + try { + // First base64 decode + let decoded = atob(encryptedString); + // Second base64 decode + decoded = atob(decoded); + // ROT13 decode + decoded = rot13(decoded); + // Third base64 decode + decoded = atob(decoded); + // Parse JSON + return JSON.parse(decoded); + } + catch (error) { + console.error("Error decoding string:", error); + return null; + } +} diff --git a/manifest.json b/manifest.json index 07ffdb5..a3b158d 100644 --- a/manifest.json +++ b/manifest.json @@ -31,6 +31,14 @@ "type": "global", "disabled": false }, + { + "display_name": "4khdHub", + "value": "4khdhub", + "version": "1.0", + "icon": "", + "type": "global", + "disabled": false + }, { "display_name": "World4uFree", "value": "world4u", diff --git a/providers/4khdhub/catalog.ts b/providers/4khdhub/catalog.ts new file mode 100644 index 0000000..99734f6 --- /dev/null +++ b/providers/4khdhub/catalog.ts @@ -0,0 +1,20 @@ +export const catalog = [ + { + title: "Popular Movies", + filter: "/category/new-movies-10810.html", + }, + { + title: "Latest TV Shows", + filter: "/category/new-series-10811.html", + }, + { + title: "Anime", + filter: "/category/anime-10812.html", + }, + { + title: "4K HDR", + filter: "/category/4k-hdr-10776.html", + }, +]; + +export const genres = []; diff --git a/providers/4khdhub/meta.ts b/providers/4khdhub/meta.ts new file mode 100644 index 0000000..fa8e57b --- /dev/null +++ b/providers/4khdhub/meta.ts @@ -0,0 +1,92 @@ +import { Info, Link, ProviderContext } from "../types"; + +export const getMeta = async function ({ + link, + providerContext, +}: { + link: string; + providerContext: ProviderContext; +}): Promise { + try { + const { axios, cheerio, getBaseUrl } = providerContext; + const baseUrl = await getBaseUrl("4khdhub"); + const url = `${baseUrl}${link}`; + const res = await axios.get(url); + const data = res.data; + const $ = cheerio.load(data); + const type = $(".season-content").length > 0 ? "series" : "movie"; + const imdbId = ""; + const title = $(".page-title").text() || ""; + const image = $(".poster-image").find("img").attr("src") || ""; + const synopsis = + $(".content-section").find("p").first().text().trim() || ""; + + // Links + const links: Link[] = []; + + if (type === "series") { + $(".season-item").map((i, element) => { + const title = $(element).find(".episode-title").text(); + let directLinks: Link["directLinks"] = []; + $(element) + .find(".episode-download-item") + .map((i, element) => { + const title = $(element) + .find(".episode-file-info") + .text() + .trim() + .replace("\n", " "); + const link = $(element) + .find(".episode-links") + .find("a:contains('HubDrive')") + .attr("href"); + console.log("title⭐", title, "link", link); + if (title && link) { + directLinks.push({ title, link }); + } + }); + if (title && directLinks.length > 0) { + links.push({ + title, + directLinks: directLinks, + }); + } + }); + } else { + $(".download-item").map((i, element) => { + const title = $(element) + .find(".flex-1.text-left.font-semibold") + .text() + .trim(); + const link = $(element) + .find(".grid.grid-cols-2.gap-2") + .find("a:contains('HubDrive')") + .attr("href"); + // console.log("title⭐", title, "link", link); + if (title && link) { + links.push({ title, directLinks: [{ title, link }] }); + } + }); + } + // console.log('multi meta', links); + + return { + title, + synopsis, + image, + imdbId, + type, + linkList: links, + }; + } catch (err) { + console.error(err); + return { + title: "", + synopsis: "", + image: "", + imdbId: "", + type: "movie", + linkList: [], + }; + } +}; diff --git a/providers/4khdhub/posts.ts b/providers/4khdhub/posts.ts new file mode 100644 index 0000000..2436e49 --- /dev/null +++ b/providers/4khdhub/posts.ts @@ -0,0 +1,81 @@ +import { Post, ProviderContext } from "../types"; + +export const getPosts = async function ({ + filter, + page, + signal, + providerContext, +}: { + filter: string; + page: number; + providerValue: string; + signal: AbortSignal; + providerContext: ProviderContext; +}): Promise { + const { getBaseUrl, cheerio } = providerContext; + const baseUrl = await getBaseUrl("4khdhub"); + const url = `${baseUrl + filter}/page/${page}.html`; + console.log("4khdhubGetPosts url", url); + return posts({ url, signal, cheerio }); +}; + +export const getSearchPosts = async function ({ + searchQuery, + page, + signal, + providerContext, +}: { + searchQuery: string; + page: number; + providerValue: string; + signal: AbortSignal; + providerContext: ProviderContext; +}): Promise { + const { getBaseUrl, cheerio } = providerContext; + const baseUrl = await getBaseUrl("4khdhub"); + const url = `${baseUrl}/page/${page}.html?s=hin${searchQuery}`; + return posts({ url, signal, cheerio }); +}; + +async function posts({ + url, + signal, + cheerio, +}: { + url: string; + signal: AbortSignal; + cheerio: ProviderContext["cheerio"]; +}): Promise { + try { + const res = await fetch(url, { signal }); + const data = await res.text(); + const $ = cheerio.load(data); + const catalog: Post[] = []; + $(".card-grid") + .children() + .map((i, element) => { + const title = $(element).find(".movie-card-title").text(); + const link = $(element).attr("href"); + const image = $(element).find("img").attr("src"); + // console.log( + // "4khdhubGetPosts title", + // title, + // "link", + // link, + // "image", + // image + // ); + if (title && link && image) { + catalog.push({ + title: title, + link: link, + image: image, + }); + } + }); + return catalog; + } catch (err) { + console.error("4khdhubGetPosts error ", err); + return []; + } +} diff --git a/providers/4khdhub/stream.ts b/providers/4khdhub/stream.ts new file mode 100644 index 0000000..62f2503 --- /dev/null +++ b/providers/4khdhub/stream.ts @@ -0,0 +1,188 @@ +import { ProviderContext } from "../types"; + +export async function getStream({ + link, + signal, + providerContext, +}: { + link: string; + type: string; + signal: AbortSignal; + providerContext: ProviderContext; +}) { + const { + axios, + cheerio, + extractors, + commonHeaders: headers, + } = providerContext; + const { hubcloudExtracter } = extractors; + let hubdriveLink = ""; + if (link.includes("hubdrive")) { + const hubdriveRes = await axios.get(link, { headers, signal }); + const hubdriveText = hubdriveRes.data; + const $ = cheerio.load(hubdriveText); + hubdriveLink = + $(".btn.btn-primary.btn-user.btn-success1.m-1").attr("href") || link; + } else { + const res = await axios.get(link, { headers, signal }); + const text = res.data; + const encryptedString = text.split("s('o','")?.[1]?.split("',180")?.[0]; + const decodedString: any = decodeString(encryptedString); + link = atob(decodedString?.o); + const redirectLink = await getRedirectLinks(link, signal, headers); + const redirectLinkRes = await axios.get(redirectLink, { headers, signal }); + const redirectLinkText = redirectLinkRes.data; + const $ = cheerio.load(redirectLinkText); + hubdriveLink = + $('h3:contains("1080p")').find("a").attr("href") || + redirectLinkText.match( + /href="(https:\/\/hubcloud\.[^\/]+\/drive\/[^"]+)"/ + )[1]; + if (hubdriveLink.includes("hubdrive")) { + const hubdriveRes = await axios.get(hubdriveLink, { headers, signal }); + const hubdriveText = hubdriveRes.data; + const $$ = cheerio.load(hubdriveText); + hubdriveLink = + $$(".btn.btn-primary.btn-user.btn-success1.m-1").attr("href") || + hubdriveLink; + } + } + const hubdriveLinkRes = await axios.get(hubdriveLink, { headers, signal }); + const hubcloudText = hubdriveLinkRes.data; + const hubcloudLink = + hubcloudText.match( + //i + )?.[1] || hubdriveLink; + try { + return await hubcloudExtracter(hubcloudLink, signal); + } catch (error: any) { + console.log("hd hub 4 getStream error: ", error); + return []; + } +} + +const encode = function (value: string) { + return btoa(value.toString()); +}; +const decode = function (value: string) { + if (value === undefined) { + return ""; + } + return atob(value.toString()); +}; +const pen = function (value: string) { + return value.replace(/[a-zA-Z]/g, function (_0x1a470e: any) { + return String.fromCharCode( + (_0x1a470e <= "Z" ? 90 : 122) >= + (_0x1a470e = _0x1a470e.charCodeAt(0) + 13) + ? _0x1a470e + : _0x1a470e - 26 + ); + }); +}; + +const abortableTimeout = ( + ms: number, + { signal }: { signal?: AbortSignal } = {} +) => { + return new Promise((resolve, reject) => { + if (signal && signal.aborted) { + return reject(new Error("Aborted")); + } + + const timer = setTimeout(resolve, ms); + + if (signal) { + signal.addEventListener("abort", () => { + clearTimeout(timer); + reject(new Error("Aborted")); + }); + } + }); +}; + +export async function getRedirectLinks( + link: string, + signal: AbortSignal, + headers: any +) { + try { + const res = await fetch(link, { headers, signal }); + const resText = await res.text(); + + var regex = /ck\('_wp_http_\d+','([^']+)'/g; + var combinedString = ""; + + var match; + while ((match = regex.exec(resText)) !== null) { + // console.log(match[1]); + combinedString += match[1]; + } + // console.log(decode(combinedString)); + const decodedString = decode(pen(decode(decode(combinedString)))); + // console.log(decodedString); + const data = JSON.parse(decodedString); + console.log(data); + const token = encode(data?.data); + const blogLink = data?.wp_http1 + "?re=" + token; + // abort timeout on signal + let wait = abortableTimeout((Number(data?.total_time) + 3) * 1000, { + signal, + }); + + await wait; + console.log("blogLink", blogLink); + + let vcloudLink = "Invalid Request"; + while (vcloudLink.includes("Invalid Request")) { + const blogRes = await fetch(blogLink, { headers, signal }); + const blogResText = (await blogRes.text()) as any; + if (blogResText.includes("Invalid Request")) { + console.log(blogResText); + } else { + vcloudLink = blogResText.match(/var reurl = "([^"]+)"/) || ""; + break; + } + } + + // console.log('vcloudLink', vcloudLink?.[1]); + return blogLink || link; + } catch (err) { + console.log("Error in getRedirectLinks", err); + return link; + } +} + +function rot13(str: string) { + return str.replace(/[a-zA-Z]/g, function (char) { + const charCode = char.charCodeAt(0); + const isUpperCase = char <= "Z"; + const baseCharCode = isUpperCase ? 65 : 97; + return String.fromCharCode( + ((charCode - baseCharCode + 13) % 26) + baseCharCode + ); + }); +} + +export function decodeString(encryptedString: string) { + try { + // First base64 decode + let decoded = atob(encryptedString); + + // Second base64 decode + decoded = atob(decoded); + + // ROT13 decode + decoded = rot13(decoded); + + // Third base64 decode + decoded = atob(decoded); + + // Parse JSON + return JSON.parse(decoded); + } catch (error) { + console.error("Error decoding string:", error); + return null; + } +}