From 4dbc0ff321e12d222885b57516ffff8d773b2e5e Mon Sep 17 00:00:00 2001 From: himanshu8443 Date: Tue, 7 Oct 2025 20:22:56 +0530 Subject: [PATCH] feat: Add initial implementation for ogomovies provider --- dist/ogomovies/catalog.js | 1 + dist/ogomovies/meta.js | 1 + dist/ogomovies/posts.js | 1 + dist/ogomovies/stream.js | 1 + providers/ogomovies/catalog.ts | 10 ++ providers/ogomovies/meta.ts | 227 +++++++++++++++++++++++++++++++++ providers/ogomovies/posts.ts | 120 +++++++++++++++++ providers/ogomovies/stream.ts | 95 ++++++++++++++ 8 files changed, 456 insertions(+) create mode 100644 dist/ogomovies/catalog.js create mode 100644 dist/ogomovies/meta.js create mode 100644 dist/ogomovies/posts.js create mode 100644 dist/ogomovies/stream.js create mode 100644 providers/ogomovies/catalog.ts create mode 100644 providers/ogomovies/meta.ts create mode 100644 providers/ogomovies/posts.ts create mode 100644 providers/ogomovies/stream.ts diff --git a/dist/ogomovies/catalog.js b/dist/ogomovies/catalog.js new file mode 100644 index 0000000..b8883e7 --- /dev/null +++ b/dist/ogomovies/catalog.js @@ -0,0 +1 @@ +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.catalog=void 0,exports.catalog=[{title:"Trending",filter:""},{title:"Movies",filter:"genre/hollywood/"}]; \ No newline at end of file diff --git a/dist/ogomovies/meta.js b/dist/ogomovies/meta.js new file mode 100644 index 0000000..93578f3 --- /dev/null +++ b/dist/ogomovies/meta.js @@ -0,0 +1 @@ +"use strict";var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){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){var value;result.done?resolve(result.value):(value=result.value,value instanceof P?value:new P(function(resolve){resolve(value)})).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};Object.defineProperty(exports,"__esModule",{value:!0}),exports.getMeta=void 0;const headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Cache-Control":"no-store","Accept-Language":"en-US,en;q=0.9",DNT:"1","sec-ch-ua":'"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',"sec-ch-ua-mobile":"?0","sec-ch-ua-platform":'"Windows"',"Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1",Cookie:"xla=s4t; _ga=GA1.1.1081149560.1756378968; _ga_BLZGKYN5PF=GS2.1.s1756378968$o1$g1$t1756378984$j44$l0$h0","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"};function getDownloadLinks(watchUrl,movieTitle,providerContext){return __awaiter(this,void 0,void 0,function*(){const{axios:axios,cheerio:cheerio}=providerContext,finalLinks=[];try{let response=yield axios.get(watchUrl,{headers:headers}),$=cheerio.load(response.data);const serverLink=$('li[data-server="3"][data-putload]').attr("data-putload");if(!serverLink)return finalLinks;response=yield axios.get(serverLink,{headers:Object.assign(Object.assign({},headers),{Referer:watchUrl})}),$=cheerio.load(response.data);const linkGateUrl=$(".content-pt a button").parent().attr("href");if(!linkGateUrl)return finalLinks;response=yield axios.get(linkGateUrl,{headers:Object.assign(Object.assign({},headers),{Referer:serverLink})}),$=cheerio.load(response.data);const finalDownloadPageUrl=$(".video-container iframe").attr("src");if(!finalDownloadPageUrl)return finalLinks;response=yield axios.get(finalDownloadPageUrl,{headers:Object.assign(Object.assign({},headers),{Referer:linkGateUrl})}),$=cheerio.load(response.data);const cdnLinkUrl=$(".content-pt a button").parent().attr("href");if(!cdnLinkUrl)return finalLinks;response=yield axios.get(cdnLinkUrl,{headers:Object.assign(Object.assign({},headers),{Referer:finalDownloadPageUrl})}),$=cheerio.load(response.data),$('button[onclick^="download_video"]').each((_,element)=>{const qualityText=$(element).text().trim(),qualityMatch=qualityText.match(/(Normal|Low)\squality/i),quality=qualityMatch?qualityMatch[1]:"Unknown",sizeMatch=qualityText.match(/(\d+(\.\d+)?\s(GB|MB))$/i),size=sizeMatch?sizeMatch[0]:"Unknown Size";finalLinks.push({title:`${movieTitle} - ${qualityText}`,quality:quality,episodesLink:cdnLinkUrl,directLinks:[{title:`Download (${size})`,link:cdnLinkUrl,type:"movie"}]})})}catch(error){}return finalLinks})}const getMeta=function(_a){return __awaiter(this,arguments,void 0,function*({link:link,providerContext:providerContext}){const{axios:axios,cheerio:cheerio}=providerContext,url=link,baseUrl=url.split("/").slice(0,3).join("/"),emptyResult={title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]};try{const response=yield axios.get(url,{headers:Object.assign(Object.assign({},headers),{Referer:baseUrl})}),$=cheerio.load(response.data),detailEl=$(".main-detail"),result={title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]};result.title=detailEl.find(".detail-mod h3").text().trim()||detailEl.find(".breadcrumb .active span[itemprop='name']").text().trim().replace("(Tamil)","").trim()||$("title").text().split("|")[0].trim(),result.image=detailEl.find(".dm-thumb img").attr("src")||"",result.image.startsWith("//")&&(result.image="https:"+result.image),result.synopsis=detailEl.find(".desc p").text().trim()||"Synopsis not found.",result.imdbId=detailEl.find("#imdb_id").text().trim(),result.type="movie";detailEl.find(".mvici-right .quality a").text().trim();let finalLinks=[];const watchLinkUrl=detailEl.find(".ch_btn_box a.bwac-btn").attr("href");if(watchLinkUrl){const deepDownloadLinks=yield getDownloadLinks(watchLinkUrl,result.title,providerContext);finalLinks=finalLinks.concat(deepDownloadLinks)}return detailEl.find(".mobile-btn a.mod-btn").each((index,element)=>{var _a;const btnEl=$(element),linkUrl=btnEl.attr("href"),rawTitle=null!==(_a=btnEl.attr("title"))&&void 0!==_a?_a:"",fallbackTitle=btnEl.text().trim(),title=rawTitle.trim()||fallbackTitle;title.includes("Download Android APP")||linkUrl&&(title.includes("Download")||title.includes("Watch")||title.includes("Join Us"))&&finalLinks.push({title:`${result.title} - ${title}`,quality:"External Link",episodesLink:linkUrl,directLinks:[{title:title,link:linkUrl,type:"movie"}]})}),result.linkList=finalLinks,result}catch(err){return emptyResult}})};exports.getMeta=getMeta; \ No newline at end of file diff --git a/dist/ogomovies/posts.js b/dist/ogomovies/posts.js new file mode 100644 index 0000000..dd0a089 --- /dev/null +++ b/dist/ogomovies/posts.js @@ -0,0 +1 @@ +"use strict";var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){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){var value;result.done?resolve(result.value):(value=result.value,value instanceof P?value:new P(function(resolve){resolve(value)})).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};Object.defineProperty(exports,"__esModule",{value:!0}),exports.getPosts=getPosts,exports.getSearchPosts=getSearchPosts;const defaultHeaders={Referer:"https://www.google.com","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language":"en-US,en;q=0.9",Pragma:"no-cache","Cache-Control":"no-cache"};function getPosts(_a){return __awaiter(this,arguments,void 0,function*({filter:filter,page:page=1,signal:signal,providerContext:providerContext}){return fetchPosts({filter:filter,page:page,query:"",signal:signal,providerContext:providerContext})})}function getSearchPosts(_a){return __awaiter(this,arguments,void 0,function*({searchQuery:searchQuery,page:page=1,signal:signal,providerContext:providerContext}){return fetchPosts({filter:"",page:page,query:searchQuery,signal:signal,providerContext:providerContext})})}function fetchPosts(_a){return __awaiter(this,arguments,void 0,function*({filter:filter,query:query,page:page=1,signal:signal,providerContext:providerContext}){try{const baseUrl="https://ogomovies.mobi";let url;if(query&&query.trim()){const encodedQuery=encodeURIComponent(query.trim());url=page>1?`${baseUrl}/search-query/${encodedQuery}/page/${page}/`:`${baseUrl}/search-query/${encodedQuery}/`}else url=filter?filter.startsWith("/")?`${baseUrl}${filter.replace(/\/$/,"")}${page>1?`/page/${page}`:""}`:`${baseUrl}/${filter}${page>1?`/page/${page}`:""}`:`${baseUrl}${page>1?`/page/${page}`:""}`;const{axios:axios,cheerio:cheerio}=providerContext,res=yield axios.get(url,{headers:defaultHeaders,signal:signal}),$=cheerio.load(res.data||""),resolveUrl=href=>(null==href?void 0:href.startsWith("http"))?href:new URL(href,baseUrl).href,seen=new Set,catalog=[];return $(".ml-item").each((_,el)=>{var _a;const anchor=$(el).find("a.ml-mask");let link=anchor.attr("href")||"";if(!link)return;if(link=resolveUrl(link),seen.has(link))return;const title=(null===(_a=anchor.attr("title"))||void 0===_a?void 0:_a.trim())||anchor.find("h2").text().trim()||"";let img=anchor.find("img").attr("data-original")||anchor.find("img").attr("src")||"";const image=img?resolveUrl(img):"";title&&image&&(seen.add(link),catalog.push({title:title,link:link,image:image}))}),catalog.slice(0,100)}catch(err){return[]}})} \ No newline at end of file diff --git a/dist/ogomovies/stream.js b/dist/ogomovies/stream.js new file mode 100644 index 0000000..0c24d07 --- /dev/null +++ b/dist/ogomovies/stream.js @@ -0,0 +1 @@ +"use strict";var __awaiter=this&&this.__awaiter||function(thisArg,_arguments,P,generator){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){var value;result.done?resolve(result.value):(value=result.value,value instanceof P?value:new P(function(resolve){resolve(value)})).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};Object.defineProperty(exports,"__esModule",{value:!0}),exports.getStream=getStream;const headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Cache-Control":"no-store","Accept-Language":"en-US,en;q=0.9",DNT:"1","sec-ch-ua":'"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"',"sec-ch-ua-mobile":"?0","sec-ch-ua-platform":'"Windows"',"Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1",Cookie:"xla=s4t; _ga=GA1.1.1081149560.1756378968; _ga_BLZGKYN5PF=GS2.1.s1756378968$o1$g1$t1756378984$j44$l0$h0","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"};function getStream(_a){return __awaiter(this,arguments,void 0,function*({link:link,type:type,signal:signal,providerContext:providerContext}){const{axios:axios,cheerio:cheerio}=providerContext;try{const streamLinks=[],dotlinkRes=yield axios(`${link}`,{headers:headers,signal:signal}),buttonMatches=dotlinkRes.data.matchAll(/download_video\('([^']+)','([^']+)','([^']+)'\)/g);for(const match of buttonMatches){const[,id,mode,hash]=match,dlUrl=`https://cdn.bewab.co/dl?op=download_orig&id=${id}&mode=${mode}&hash=${hash}`;try{const dlText=(yield axios(dlUrl,{headers:headers,signal:signal})).data,$$=cheerio.load(dlText),directMatches=dlText.matchAll(/{var _a;const href=null!==(_a=$$(el).attr("href"))&&void 0!==_a?_a:null;href&&(href.includes(".mkv")||href.includes(".mp4"))&&streamLinks.push({server:"direct",link:href,type:href.endsWith(".mp4")?"mp4":"mkv"})})}catch(err){}}return streamLinks}catch(error){return[]}})} \ No newline at end of file diff --git a/providers/ogomovies/catalog.ts b/providers/ogomovies/catalog.ts new file mode 100644 index 0000000..d6950a0 --- /dev/null +++ b/providers/ogomovies/catalog.ts @@ -0,0 +1,10 @@ +export const catalog = [ + { + title: "Trending", + filter: "", + }, + { + title: "Movies", + filter: "genre/hollywood/", + }, +]; \ No newline at end of file diff --git a/providers/ogomovies/meta.ts b/providers/ogomovies/meta.ts new file mode 100644 index 0000000..bde5190 --- /dev/null +++ b/providers/ogomovies/meta.ts @@ -0,0 +1,227 @@ +import { Info, Link, ProviderContext } from "../types"; + +// Headers (omitted for brevity, assume they are the same) +const headers = { + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Cache-Control": "no-store", + "Accept-Language": "en-US,en;q=0.9", + DNT: "1", + "sec-ch-ua": + '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + Cookie: + "xla=s4t; _ga=GA1.1.1081149560.1756378968; _ga_BLZGKYN5PF=GS2.1.s1756378968$o1$g1$t1756378984$j44$l0$h0", + "Upgrade-Insecure-Requests": "1", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0", +}; + +/** + * Executes the 5-step link chaining to fetch the final download links. + */ +async function getDownloadLinks( + watchUrl: string, + movieTitle: string, + providerContext: ProviderContext +): Promise { + const { axios, cheerio } = providerContext; + const finalLinks: Link[] = []; + + try { + // --- STEP 1: Scrape Watch Page to get Server Link (data-putload) --- + let response = await axios.get(watchUrl, { headers }); + let $ = cheerio.load(response.data); + + // Find the specific episode item and extract the data-putload URL + const serverLink = $('li[data-server="3"][data-putload]').attr('data-putload'); + + if (!serverLink) { + console.log("Failed to find server link (data-putload) on watch page."); + return finalLinks; + } + + // --- STEP 2: Scrape Server Link Page to get Link Gate Button URL --- + response = await axios.get(serverLink, { headers: { ...headers, Referer: watchUrl } }); + $ = cheerio.load(response.data); + + // Find the "GET DOWNLOAD LINKS" button URL + const linkGateUrl = $('.content-pt a button').parent().attr('href'); + + if (!linkGateUrl) { + console.log("Failed to find link gate URL on server page."); + return finalLinks; + } + + // --- STEP 3: Scrape Link Gate Page to get Iframe Source (Final Download Page) --- + response = await axios.get(linkGateUrl, { headers: { ...headers, Referer: serverLink } }); + $ = cheerio.load(response.data); + + // Find the iframe src (the final download page URL) + const finalDownloadPageUrl = $('.video-container iframe').attr('src'); + + if (!finalDownloadPageUrl) { + console.log("Failed to find final download page URL (iframe src)."); + return finalLinks; + } + + // --- STEP 4: Scrape Final Download Page to get CDN Button URL --- + response = await axios.get(finalDownloadPageUrl, { headers: { ...headers, Referer: linkGateUrl } }); + $ = cheerio.load(response.data); + + // Find the CDN button URL + const cdnLinkUrl = $('.content-pt a button').parent().attr('href'); + + if (!cdnLinkUrl) { + console.log("Failed to find CDN link URL on final download page."); + return finalLinks; + } + + // --- STEP 5: Scrape CDN Page to get Final Download Buttons --- + response = await axios.get(cdnLinkUrl, { headers: { ...headers, Referer: finalDownloadPageUrl } }); + $ = cheerio.load(response.data); + + // Extract the final direct download buttons + $('button[onclick^="download_video"]').each((_, element) => { + const btnEl = $(element); + const qualityText = btnEl.text().trim(); // e.g., "Normal quality 1128x480, 1.0 GB" + + // Extract Quality (e.g., Normal/Low) and Size (e.g., 1.0 GB) + const qualityMatch = qualityText.match(/(Normal|Low)\squality/i); + const quality = qualityMatch ? qualityMatch[1] : 'Unknown'; + const sizeMatch = qualityText.match(/(\d+(\.\d+)?\s(GB|MB))$/i); + const size = sizeMatch ? sizeMatch[0] : 'Unknown Size'; + + // Construct link object for final download buttons + finalLinks.push({ + title: `${movieTitle} - ${qualityText}`, + // Use extracted quality (Normal/Low) + quality: quality, + // episodesLink points to the final button page (since direct link is JS-driven) + episodesLink: cdnLinkUrl, + directLinks: [ + { + title: `Download (${size})`, + // Use the button page as the link (requires further processing if a direct file link is needed) + link: cdnLinkUrl, + type: "movie", + }, + ], + }); + }); + + } catch (error) { + console.error("Error during link chaining:", error); + } + + return finalLinks; +} + + +export const getMeta = async function ({ + link, + providerContext, +}: { + link: string; + providerContext: ProviderContext; +}): Promise { + const { axios, cheerio } = providerContext; + const url = link; + const baseUrl = url.split("/").slice(0, 3).join("/"); + + const emptyResult: Info = { + title: "", + synopsis: "", + image: "", + imdbId: "", + type: "movie", + linkList: [], + }; + + try { + const response = await axios.get(url, { + headers: { ...headers, Referer: baseUrl }, + }); + + const $ = cheerio.load(response.data); + const detailEl = $(".main-detail"); + + const result: Info = { + title: "", + synopsis: "", + image: "", + imdbId: "", + type: "movie", + linkList: [], + }; + + // --- Metadata Extraction --- + result.title = detailEl.find(".detail-mod h3").text().trim() || + detailEl.find(".breadcrumb .active span[itemprop='name']").text().trim().replace('(Tamil)', '').trim() || + $("title").text().split("|")[0].trim(); + + result.image = detailEl.find(".dm-thumb img").attr("src") || ""; + if (result.image.startsWith("//")) result.image = "https:" + result.image; + + result.synopsis = detailEl.find(".desc p").text().trim() || "Synopsis not found."; + result.imdbId = detailEl.find("#imdb_id").text().trim(); + result.type = "movie"; + + const qualityText = detailEl.find(".mvici-right .quality a").text().trim() || "Unknown"; + + // --- LinkList Aggregation --- + let finalLinks: Link[] = []; + + // 1. Fetch deep download links + const watchButton = detailEl.find(".ch_btn_box a.bwac-btn"); + const watchLinkUrl = watchButton.attr("href"); + + if (watchLinkUrl) { + const deepDownloadLinks = await getDownloadLinks(watchLinkUrl, result.title, providerContext); + finalLinks = finalLinks.concat(deepDownloadLinks); + } + + // 2. Fetch External Links (excluding "Download Android APP") + detailEl.find(".mobile-btn a.mod-btn").each((index, element) => { + const btnEl = $(element); + const linkUrl = btnEl.attr("href"); + + const rawTitle = btnEl.attr("title") ?? ''; + const fallbackTitle = btnEl.text().trim(); + const title = rawTitle.trim() || fallbackTitle; + + // EXCLUSION: Skip the Android App link + if (title.includes('Download Android APP')) { + return; + } + + if (linkUrl && (title.includes('Download') || title.includes('Watch') || title.includes('Join Us'))) { + finalLinks.push({ + title: `${result.title} - ${title}`, + quality: 'External Link', + episodesLink: linkUrl, + directLinks: [ + { + title: title, + link: linkUrl, + type: "movie", + } + ] + }); + } + }); + + + result.linkList = finalLinks; + + return result; + } catch (err) { + console.log("getMeta error:", err); + return emptyResult; + } +}; \ No newline at end of file diff --git a/providers/ogomovies/posts.ts b/providers/ogomovies/posts.ts new file mode 100644 index 0000000..fdddd11 --- /dev/null +++ b/providers/ogomovies/posts.ts @@ -0,0 +1,120 @@ +import { Post, ProviderContext } from "../types"; + +const defaultHeaders = { + Referer: "https://www.google.com", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.9", + Pragma: "no-cache", + "Cache-Control": "no-cache", +}; + +// --- Normal catalog posts --- +export async function getPosts({ + filter, + page = 1, + signal, + providerContext, +}: { + filter?: string; + page?: number; + signal?: AbortSignal; + providerContext: ProviderContext; +}): Promise { + return fetchPosts({ filter, page, query: "", signal, providerContext }); +} + +// --- Search posts --- +export async function getSearchPosts({ + searchQuery, + page = 1, + signal, + providerContext, +}: { + searchQuery: string; + page?: number; + signal?: AbortSignal; + providerContext: ProviderContext; +}): Promise { + return fetchPosts({ filter: "", page, query: searchQuery, signal, providerContext }); +} + +// --- Core function --- +async function fetchPosts({ + filter, + query, + page = 1, + signal, + providerContext, +}: { + filter?: string; + query?: string; + page?: number; + signal?: AbortSignal; + providerContext: ProviderContext; +}): Promise { + try { + const baseUrl = "https://ogomovies.mobi"; + let url: string; + + if (query && query.trim()) { + // ✅ OGOMovies new search URL + const encodedQuery = encodeURIComponent(query.trim()); + url = + page > 1 + ? `${baseUrl}/search-query/${encodedQuery}/page/${page}/` + : `${baseUrl}/search-query/${encodedQuery}/`; + } else if (filter) { + url = filter.startsWith("/") + ? `${baseUrl}${filter.replace(/\/$/, "")}${page > 1 ? `/page/${page}` : ""}` + : `${baseUrl}/${filter}${page > 1 ? `/page/${page}` : ""}`; + } else { + url = `${baseUrl}${page > 1 ? `/page/${page}` : ""}`; + } + + const { axios, cheerio } = providerContext; + const res = await axios.get(url, { headers: defaultHeaders, signal }); + const $ = cheerio.load(res.data || ""); + + const resolveUrl = (href: string) => + href?.startsWith("http") ? href : new URL(href, baseUrl).href; + + const seen = new Set(); + const catalog: Post[] = []; + + // ✅ OGOMovies structure -> .ml-item + $(".ml-item").each((_, el) => { + const anchor = $(el).find("a.ml-mask"); + let link = anchor.attr("href") || ""; + if (!link) return; + link = resolveUrl(link); + if (seen.has(link)) return; + + // Title + const title = + anchor.attr("title")?.trim() || anchor.find("h2").text().trim() || ""; + + // Image + let img = + anchor.find("img").attr("data-original") || + anchor.find("img").attr("src") || + ""; + const image = img ? resolveUrl(img) : ""; + + if (!title || !image) return; + + seen.add(link); + catalog.push({ title, link, image }); + }); + + return catalog.slice(0, 100); + } catch (err) { + console.error( + "fetchPosts error:", + err instanceof Error ? err.message : String(err) + ); + return []; + } +} diff --git a/providers/ogomovies/stream.ts b/providers/ogomovies/stream.ts new file mode 100644 index 0000000..7e08b29 --- /dev/null +++ b/providers/ogomovies/stream.ts @@ -0,0 +1,95 @@ +import { ProviderContext, Stream } from "../types"; + +const headers = { + Accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Cache-Control": "no-store", + "Accept-Language": "en-US,en;q=0.9", + DNT: "1", + "sec-ch-ua": + '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", + Cookie: + "xla=s4t; _ga=GA1.1.1081149560.1756378968; _ga_BLZGKYN5PF=GS2.1.s1756378968$o1$g1$t1756378984$j44$l0$h0", + "Upgrade-Insecure-Requests": "1", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0", +}; + +export async function getStream({ + link, + type, + signal, + providerContext, +}: { + link: string; + type: string; + signal: AbortSignal; + providerContext: ProviderContext; +}) { + const { axios, cheerio } = providerContext; + + try { + const streamLinks: Stream[] = []; + + // 🔹 Step 1: Load main page + const dotlinkRes = await axios(`${link}`, { headers, signal }); + const dotlinkText = dotlinkRes.data; + + // 🔹 Step 2: Extract button download_video() info + const buttonMatches = dotlinkText.matchAll( + /download_video\('([^']+)','([^']+)','([^']+)'\)/g + ); + + for (const match of buttonMatches) { + const [, id, mode, hash] = match; + const dlUrl = `https://cdn.bewab.co/dl?op=download_orig&id=${id}&mode=${mode}&hash=${hash}`; + + // 🔹 Step 3: Visit dl page and extract only final direct link + try { + const dlRes = await axios(dlUrl, { headers, signal }); + const dlText = dlRes.data; + const $$ = cheerio.load(dlText); + + // Regex scrape + const directMatches = dlText.matchAll( + / { + const href = $$(el).attr("href") ?? null; + if (href && (href.includes(".mkv") || href.includes(".mp4"))) { + streamLinks.push({ + server: "direct", + link: href, + type: href.endsWith(".mp4") ? "mp4" : "mkv", + }); + } + }); + } catch (err: any) { + console.log("❌ error loading dl page:", err.message); + } + } + + return streamLinks; + } catch (error: any) { + console.log("getStream error: ", error.message); + return []; + } +}