mirror of
https://github.com/vega-org/vega-providers.git
synced 2026-04-17 15:41:45 +00:00
feat: Add new provider implementations for Joya9tv and skyMovieHD
This commit is contained in:
1
dist/Joya9tv/catalog.js
vendored
Normal file
1
dist/Joya9tv/catalog.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.catalog=void 0,exports.catalog=[{title:"Latest",filter:""},{title:"Bangali-Movies",filter:"genre/bengali-movies/"}];
|
||||
1
dist/Joya9tv/episodes.js
vendored
Normal file
1
dist/Joya9tv/episodes.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getEpisodes=void 0;const getEpisodes=function({url:url,providerContext:providerContext}){const{axios:axios,cheerio:cheerio,commonHeaders:headers}=providerContext;return axios.get(url,{headers:headers}).then(res=>{const $=cheerio.load(res.data),container=$("ul:has(p.font-bold:contains('Episode'))").first(),episodes=[];return container.find("p.font-bold").each((_,element)=>{const el=$(element);let title=el.text().trim();if(!title)return;let currentElement=el.parent();for(;currentElement.next().length&&!currentElement.next().find("p.font-bold").length;)currentElement=currentElement.next(),currentElement.find("a[href]").each((_,a)=>{var _a;const href=null===(_a=$(a).attr("href"))||void 0===_a?void 0:_a.trim();href&&(href.includes("hubcloud.one")||href.includes("gdflix.dev"))&&episodes.push({title:title.replace(/ Links$/i,""),link:href})})}),episodes}).catch(err=>[])};exports.getEpisodes=getEpisodes;
|
||||
1
dist/Joya9tv/meta.js
vendored
Normal file
1
dist/Joya9tv/meta.js
vendored
Normal file
@@ -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"},getMeta=function(_a){return __awaiter(this,arguments,void 0,function*({link:link,providerContext:providerContext}){var _b;const{cheerio:cheerio}=providerContext,url=link,baseUrl=url.split("/").slice(0,3).join("/"),emptyResult={title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]};try{const response=yield fetch(url,{headers:Object.assign(Object.assign({},headers),{Referer:baseUrl})}),data=yield response.text(),$=cheerio.load(data),infoContainer=$(".content.right").first(),result={title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]};/S\d+|Season \d+|TV Series\/Shows/i.test(infoContainer.find("h1").text()+$(".sgeneros").text())?result.type="series":result.type="movie";const rawTitle=$("h1").first().text().trim();let finalTitle=rawTitle.replace(/ Download.*|\[Episode \d+ Added\]/g,"").trim();finalTitle=finalTitle.split(/\(2025\)| S\d+/i)[0].trim()||"Unknown Title",result.title=finalTitle;const imdbMatch=null===(_b=infoContainer.html())||void 0===_b?void 0:_b.match(/tt\d+/);result.imdbId=imdbMatch?imdbMatch[0]:"";let image=infoContainer.find(".poster img[src]").first().attr("src")||"";image.startsWith("//")&&(image="https:"+image),(image.includes("no-thumbnail")||image.includes("placeholder"))&&(image=""),result.image=image,result.synopsis=$("#info .wp-content").text().trim()||"";const links=[];return $("#download .links_table table tbody").find("tr").each((index,element)=>{var _a;const row=$(element),quality=row.find("strong.quality").text().trim(),size=row.find("td:nth-child(4)").text().trim(),directLinkAnchor=row.find("td a").first(),directLink=directLinkAnchor.attr("href"),linkTitle=directLinkAnchor.text().trim();if(quality&&directLink){const directLinks=[{title:linkTitle||"Download Link",link:directLink,type:result.type}],seasonMatch=null===(_a=rawTitle.match(/S(\d+)/))||void 0===_a?void 0:_a[1];let fullTitle=`${result.title}`;seasonMatch&&(fullTitle+=` Season ${seasonMatch}`),fullTitle+=` - ${quality}`,size&&(fullTitle+=` (${size})`),links.push({title:fullTitle,quality:quality.replace(/[^0-9p]/g,""),episodesLink:directLink,directLinks:directLinks})}}),result.linkList=links,result}catch(err){return emptyResult}})};exports.getMeta=getMeta;
|
||||
1
dist/Joya9tv/posts.js
vendored
Normal file
1
dist/Joya9tv/posts.js
vendored
Normal file
@@ -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=yield providerContext.getBaseUrl("joya9tv");let url;if(query&&query.trim()&&"what are you looking for?"!==query.trim().toLowerCase()){const params=new URLSearchParams;params.append("s",query.trim()),page>1&¶ms.append("paged",page.toString()),url=`${baseUrl}/?${params.toString()}`}else url=filter?filter.startsWith("/")?`${baseUrl}${filter.replace(/\/$/,"")}${page>1?`/page/${page}`:""}`:`${baseUrl}/${filter}${page>1?`/page/${page}`:""}`:`${baseUrl}${page>1?`/page/${page}`:""}`;const{cheerio:cheerio}=providerContext,res=yield fetch(url,{headers:defaultHeaders,signal:signal}),data=yield res.text(),$=cheerio.load(data||""),resolveUrl=href=>(null==href?void 0:href.startsWith("http"))?href:new URL(href,baseUrl).href,seen=new Set,catalog=[];return $("article.item.movies").each((_,el)=>{const card=$(el);let link=card.find("div.data h3 a").attr("href")||"";if(!link)return;if(link=resolveUrl(link),seen.has(link))return;let title=card.find("div.data h3 a").text().trim();if(!title)return;let img=card.find("div.poster img").attr("src")||"";const image=img?resolveUrl(img):"";seen.add(link),catalog.push({title:title,link:link,image:image})}),$(".result-item article").each((_,el)=>{const card=$(el);let link=card.find("a").attr("href")||"";if(!link)return;if(link=resolveUrl(link),seen.has(link))return;let title=card.find("a").attr("title")||card.find("img").attr("alt")||"";if(title=title.trim(),!title)return;let img=card.find("img").attr("src")||"";const image=img?resolveUrl(img):"";seen.add(link),catalog.push({title:title,link:link,image:image})}),catalog.slice(0,100)}catch(err){return[]}})}
|
||||
1
dist/Joya9tv/stream.js
vendored
Normal file
1
dist/Joya9tv/stream.js
vendored
Normal file
@@ -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","accept-language":"en-US,en;q=0.9,en-IN;q=0.8","cache-control":"no-cache",pragma:"no-cache",priority:"u=0, i","sec-ch-ua":'"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',"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","upgrade-insecure-requests":"1"};function getStream(_a){return __awaiter(this,arguments,void 0,function*({link:link,type:type,signal:signal,providerContext:providerContext}){var _b,_c,_d,_e;const{axios:axios,cheerio:cheerio,extractors:extractors}=providerContext,{hubcloudExtracter:hubcloudExtracter}=extractors;try{const streamLinks=[];if("movie"===type){const dotlinkRes=yield fetch(`${link}`,{headers:headers}),dotlinkText=yield dotlinkRes.text();link=(dotlinkText.match(/<a\s+href="([^"]*cloud\.[^"]*)"/i)||[])[1];try{const filepressLink=cheerio.load(dotlinkText)('.btn.btn-sm.btn-outline[style="background:linear-gradient(135deg,rgb(252,185,0) 0%,rgb(0,0,0)); color: #fdf8f2;"]').parent().attr("href"),filepressID=null==filepressLink?void 0:filepressLink.split("/").pop(),filepressBaseUrl=null==filepressLink?void 0:filepressLink.split("/").slice(0,-2).join("/"),filepressTokenRes=yield axios.post(filepressBaseUrl+"/api/file/downlaod/",{id:filepressID,method:"indexDownlaod",captchaValue:null},{headers:{"Content-Type":"application/json",Referer:filepressBaseUrl}});if(null===(_b=filepressTokenRes.data)||void 0===_b?void 0:_b.status){const filepressToken=null===(_c=filepressTokenRes.data)||void 0===_c?void 0:_c.data,filepressStreamLink=yield axios.post(filepressBaseUrl+"/api/file/downlaod2/",{id:filepressToken,method:"indexDownlaod",captchaValue:null},{headers:{"Content-Type":"application/json",Referer:filepressBaseUrl}});streamLinks.push({server:"filepress",link:null===(_e=null===(_d=filepressStreamLink.data)||void 0===_d?void 0:_d.data)||void 0===_e?void 0:_e[0],type:"mkv"})}}catch(error){}}return yield hubcloudExtracter(link,signal)}catch(error){return error.message.includes("Aborted"),[]}})}
|
||||
1
dist/skyMovieHD/catalog.js
vendored
Normal file
1
dist/skyMovieHD/catalog.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.catalog=void 0,exports.catalog=[{title:"Trending",filter:""},{title:"JIo-Studios",filter:"category/jio-studios/"}];
|
||||
1
dist/skyMovieHD/episodes.js
vendored
Normal file
1
dist/skyMovieHD/episodes.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getEpisodes=void 0;const getEpisodes=function({url:url,providerContext:providerContext}){const{axios:axios,cheerio:cheerio,commonHeaders:headers}=providerContext;return axios.get(url,{headers:headers}).then(res=>{const $=cheerio.load(res.data),container=$(".entry-content, .entry-inner");$(".unili-content, .code-block-1").remove();const episodes=[];return container.find("h4, h3").each((_,element)=>{const el=$(element);let title=el.text().replace(/[-:]/g,"").trim();title&&el.next("p").find("a[href*='vcloud.lol']").each((_,a)=>{var _a;const href=null===(_a=$(a).attr("href"))||void 0===_a?void 0:_a.trim();href&&episodes.push({title:title,link:href})})}),episodes}).catch(err=>[])};exports.getEpisodes=getEpisodes;
|
||||
1
dist/skyMovieHD/meta.js
vendored
Normal file
1
dist/skyMovieHD/meta.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/skyMovieHD/posts.js
vendored
Normal file
1
dist/skyMovieHD/posts.js
vendored
Normal file
@@ -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://skymovieshd.tattoo";let url;if(query&&query.trim()&&"what are you looking for?"!==query.trim().toLowerCase()){const params=new URLSearchParams;params.append("s",query.trim()),page>1&¶ms.append("paged",page.toString()),url=`${baseUrl}/?${params.toString()}`}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 $("article.latestpost").each((_,el)=>{const card=$(el);let link=card.find("header.entry-header h2.entry-title a, header.entry-header h1.entry-title a").attr("href")||"";if(!link)return;if(link=resolveUrl(link),seen.has(link))return;let title=card.find("header.entry-header h2.entry-title a, header.entry-header h1.entry-title a").text().replace(/^Download\s*/i,"").trim();if(!title)return;let img=card.find("a#featured-thumbnail img").attr("data-src")||card.find("a#featured-thumbnail img").attr("src")||"";const image=img?resolveUrl(img):"";seen.add(link),catalog.push({title:title,link:link,image:image})}),catalog.slice(0,100)}catch(err){return[]}})}
|
||||
1
dist/skyMovieHD/stream.js
vendored
Normal file
1
dist/skyMovieHD/stream.js
vendored
Normal file
@@ -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,extractors:extractors}=providerContext,{hubcloudExtracter:hubcloudExtracter}=extractors;try{return yield hubcloudExtracter(link,signal)}catch(error){return error.message.includes("Aborted"),[]}})}
|
||||
10
providers/Joya9tv/catalog.ts
Normal file
10
providers/Joya9tv/catalog.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const catalog = [
|
||||
{
|
||||
title: "Latest",
|
||||
filter: "", // baseUrl se latest page fetch hoga
|
||||
},
|
||||
{
|
||||
title: "Bangali-Movies",
|
||||
filter: "genre/bengali-movies/",
|
||||
},
|
||||
];
|
||||
58
providers/Joya9tv/episodes.ts
Normal file
58
providers/Joya9tv/episodes.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { EpisodeLink, ProviderContext } from "../types";
|
||||
|
||||
export const getEpisodes = function ({
|
||||
url,
|
||||
providerContext,
|
||||
}: {
|
||||
url: string;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<EpisodeLink[]> {
|
||||
const { axios, cheerio, commonHeaders: headers } = providerContext;
|
||||
console.log("getEpisodeLinks", url);
|
||||
|
||||
return axios
|
||||
.get(url, { headers })
|
||||
.then((res) => {
|
||||
const $ = cheerio.load(res.data);
|
||||
// Target the container that holds the episode links (based on the provided sample)
|
||||
const container = $("ul:has(p.font-bold:contains('Episode'))").first();
|
||||
|
||||
const episodes: EpisodeLink[] = [];
|
||||
|
||||
// Find all bold episode link headings (e.g., 'Episode 38 Links 480p')
|
||||
container.find("p.font-bold").each((_, element) => {
|
||||
const el = $(element);
|
||||
let title = el.text().trim(); // e.g., "Episode 38 Links 480p"
|
||||
if (!title) return;
|
||||
|
||||
// Use a selector for the direct links that follow this title (in the next siblings)
|
||||
// The episode links are in <li> elements directly following the <p class="font-bold">
|
||||
let currentElement = el.parent(); // Get the parent <li> of the <p>
|
||||
|
||||
// Loop through the siblings until the next <p class="font-bold"> (the start of the next episode)
|
||||
while (currentElement.next().length && !currentElement.next().find("p.font-bold").length) {
|
||||
currentElement = currentElement.next();
|
||||
// Find all anchor tags (links) in the current <li> sibling
|
||||
currentElement.find("a[href]").each((_, a) => {
|
||||
const anchor = $(a);
|
||||
const href = anchor.attr("href")?.trim();
|
||||
|
||||
// Only include links for hubcloud and gdflix as requested
|
||||
if (href && (href.includes("hubcloud.one") || href.includes("gdflix.dev"))) {
|
||||
// Clean up the title to be just "Episode X 480p"
|
||||
episodes.push({
|
||||
title: title.replace(/ Links$/i, ''),
|
||||
link: href
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return episodes;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("getEpisodeLinks error:", err);
|
||||
return [];
|
||||
});
|
||||
};
|
||||
160
providers/Joya9tv/meta.ts
Normal file
160
providers/Joya9tv/meta.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { Info, Link, ProviderContext } from "../types";
|
||||
|
||||
// Headers
|
||||
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 const getMeta = async function ({
|
||||
link,
|
||||
providerContext,
|
||||
}: {
|
||||
link: string;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<Info> {
|
||||
const { 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 fetch(url, {
|
||||
headers: { ...headers, Referer: baseUrl },
|
||||
});
|
||||
|
||||
const data = await response.text();
|
||||
const $ = cheerio.load(data);
|
||||
// Use the main container from the new HTML structure
|
||||
const infoContainer = $(".content.right").first();
|
||||
|
||||
const result: Info = {
|
||||
title: "",
|
||||
synopsis: "",
|
||||
image: "",
|
||||
imdbId: "",
|
||||
type: "movie",
|
||||
linkList: [],
|
||||
};
|
||||
|
||||
// --- Type determination (Based on content, the HTML is for a Series) ---
|
||||
// Check for 'S' or 'Season' in the main heading
|
||||
if (
|
||||
/S\d+|Season \d+|TV Series\/Shows/i.test(
|
||||
infoContainer.find("h1").text() + $(".sgeneros").text()
|
||||
)
|
||||
) {
|
||||
result.type = "series";
|
||||
} else {
|
||||
result.type = "movie";
|
||||
}
|
||||
|
||||
// --- Title ---
|
||||
const rawTitle = $("h1").first().text().trim();
|
||||
|
||||
// Clean up title (remove 'Download', site name, quality/episode tags)
|
||||
let finalTitle = rawTitle
|
||||
.replace(/ Download.*|\[Episode \d+ Added\]/g, "")
|
||||
.trim();
|
||||
|
||||
// Extract base title before S19, (2025), etc.
|
||||
finalTitle =
|
||||
finalTitle.split(/\(2025\)| S\d+/i)[0].trim() || "Unknown Title";
|
||||
result.title = finalTitle;
|
||||
|
||||
// --- IMDb ID ---
|
||||
// The new HTML doesn't explicitly show an IMDb ID, so we'll rely on a more generic search.
|
||||
const imdbMatch = infoContainer.html()?.match(/tt\d+/);
|
||||
result.imdbId = imdbMatch ? imdbMatch[0] : "";
|
||||
|
||||
// --- Image ---
|
||||
let image =
|
||||
infoContainer.find(".poster img[src]").first().attr("src") || "";
|
||||
if (image.startsWith("//")) image = "https:" + image;
|
||||
|
||||
// Check for "no-thumbnail" or "placeholder" in the filename
|
||||
if (image.includes("no-thumbnail") || image.includes("placeholder"))
|
||||
image = "";
|
||||
result.image = image;
|
||||
|
||||
// --- Synopsis ---
|
||||
// The synopsis is directly in the <div itemprop="description" class="wp-content"> inside #info
|
||||
result.synopsis = $("#info .wp-content").text().trim() || "";
|
||||
|
||||
// --- LinkList extraction (Updated for the <table> structure in #download) ---
|
||||
const links: Link[] = [];
|
||||
const downloadTable = $("#download .links_table table tbody");
|
||||
|
||||
// The entire season/series batch links are in the table
|
||||
downloadTable.find("tr").each((index, element) => {
|
||||
const row = $(element);
|
||||
const quality = row.find("strong.quality").text().trim();
|
||||
|
||||
// Get the size from the fourth <td> in the row
|
||||
const size = row.find("td:nth-child(4)").text().trim();
|
||||
|
||||
const directLinkAnchor = row.find("td a").first();
|
||||
const directLink = directLinkAnchor.attr("href");
|
||||
const linkTitle = directLinkAnchor.text().trim();
|
||||
|
||||
if (quality && directLink) {
|
||||
// FIX: Assert the type to satisfy the Link interface's literal type requirement
|
||||
const assertedType = result.type as "movie" | "series";
|
||||
|
||||
// Assuming the table links are for the entire batch/season
|
||||
const directLinks = [
|
||||
{
|
||||
title: linkTitle || "Download Link",
|
||||
link: directLink,
|
||||
type: assertedType, // Use the asserted type
|
||||
},
|
||||
];
|
||||
|
||||
// Combine title, quality, and size for the LinkList entry
|
||||
const seasonMatch = rawTitle.match(/S(\d+)/)?.[1];
|
||||
let fullTitle = `${result.title}`;
|
||||
if (seasonMatch) fullTitle += ` Season ${seasonMatch}`;
|
||||
fullTitle += ` - ${quality}`;
|
||||
if (size) fullTitle += ` (${size})`; // ADDED: Append size to the link title
|
||||
|
||||
links.push({
|
||||
title: fullTitle,
|
||||
quality: quality.replace(/[^0-9p]/g, ""), // Clean to just 480p, 720p, 1080p
|
||||
// The direct link is to a page that lists all episodes, so it acts as the episodesLink
|
||||
episodesLink: directLink,
|
||||
directLinks,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
result.linkList = links;
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.log("getMeta error:", err);
|
||||
return emptyResult;
|
||||
}
|
||||
};
|
||||
148
providers/Joya9tv/posts.ts
Normal file
148
providers/Joya9tv/posts.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
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<Post[]> {
|
||||
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<Post[]> {
|
||||
return fetchPosts({
|
||||
filter: "",
|
||||
page,
|
||||
query: searchQuery,
|
||||
signal,
|
||||
providerContext,
|
||||
});
|
||||
}
|
||||
|
||||
// --- Core fetch function ---
|
||||
async function fetchPosts({
|
||||
filter,
|
||||
query,
|
||||
page = 1,
|
||||
signal,
|
||||
providerContext,
|
||||
}: {
|
||||
filter?: string;
|
||||
query?: string;
|
||||
page?: number;
|
||||
signal?: AbortSignal;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<Post[]> {
|
||||
try {
|
||||
const baseUrl = await providerContext.getBaseUrl("joya9tv");
|
||||
let url: string;
|
||||
|
||||
if (
|
||||
query &&
|
||||
query.trim() &&
|
||||
query.trim().toLowerCase() !== "what are you looking for?"
|
||||
) {
|
||||
const params = new URLSearchParams();
|
||||
params.append("s", query.trim());
|
||||
if (page > 1) params.append("paged", page.toString());
|
||||
url = `${baseUrl}/?${params.toString()}`;
|
||||
} 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 { cheerio } = providerContext;
|
||||
const res = await fetch(url, { headers: defaultHeaders, signal });
|
||||
const data = await res.text();
|
||||
const $ = cheerio.load(data || "");
|
||||
|
||||
const resolveUrl = (href: string) =>
|
||||
href?.startsWith("http") ? href : new URL(href, baseUrl).href;
|
||||
|
||||
const seen = new Set<string>();
|
||||
const catalog: Post[] = [];
|
||||
|
||||
// ✅ Case 1: Normal catalog listing
|
||||
$("article.item.movies").each((_, el) => {
|
||||
const card = $(el);
|
||||
|
||||
let link = card.find("div.data h3 a").attr("href") || "";
|
||||
if (!link) return;
|
||||
link = resolveUrl(link);
|
||||
if (seen.has(link)) return;
|
||||
|
||||
let title = card.find("div.data h3 a").text().trim();
|
||||
if (!title) return;
|
||||
|
||||
let img = card.find("div.poster img").attr("src") || "";
|
||||
const image = img ? resolveUrl(img) : "";
|
||||
|
||||
seen.add(link);
|
||||
catalog.push({ title, link, image });
|
||||
});
|
||||
|
||||
// ✅ Case 2: Search results
|
||||
$(".result-item article").each((_, el) => {
|
||||
const card = $(el);
|
||||
|
||||
let link = card.find("a").attr("href") || "";
|
||||
if (!link) return;
|
||||
link = resolveUrl(link);
|
||||
if (seen.has(link)) return;
|
||||
|
||||
let title =
|
||||
card.find("a").attr("title") || card.find("img").attr("alt") || "";
|
||||
title = title.trim();
|
||||
if (!title) return;
|
||||
|
||||
let img = card.find("img").attr("src") || "";
|
||||
const image = img ? resolveUrl(img) : "";
|
||||
|
||||
seen.add(link);
|
||||
catalog.push({ title, link, image });
|
||||
});
|
||||
|
||||
console.log(`fetchPosts: Fetched ${catalog.length} posts from ${url}`);
|
||||
|
||||
return catalog.slice(0, 100);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"fetchPosts error:",
|
||||
err instanceof Error ? err.message : String(err)
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
114
providers/Joya9tv/stream.ts
Normal file
114
providers/Joya9tv/stream.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
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",
|
||||
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
||||
"cache-control": "no-cache",
|
||||
pragma: "no-cache",
|
||||
priority: "u=0, i",
|
||||
"sec-ch-ua":
|
||||
'"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
|
||||
"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",
|
||||
"upgrade-insecure-requests": "1",
|
||||
};
|
||||
|
||||
export async function getStream({
|
||||
link,
|
||||
type,
|
||||
signal,
|
||||
providerContext,
|
||||
}: {
|
||||
link: string;
|
||||
type: string;
|
||||
signal: AbortSignal;
|
||||
providerContext: ProviderContext;
|
||||
}) {
|
||||
const { axios, cheerio, extractors } = providerContext;
|
||||
const { hubcloudExtracter } = extractors;
|
||||
try {
|
||||
const streamLinks: Stream[] = [];
|
||||
console.log("dotlink", link);
|
||||
if (type === "movie") {
|
||||
// vlink
|
||||
const dotlinkRes = await fetch(`${link}`, { headers });
|
||||
const dotlinkText = await dotlinkRes.text();
|
||||
// console.log('dotlinkText', dotlinkText);
|
||||
const vlink = dotlinkText.match(/<a\s+href="([^"]*cloud\.[^"]*)"/i) || [];
|
||||
// console.log('vLink', vlink[1]);
|
||||
link = vlink[1];
|
||||
|
||||
// filepress link
|
||||
try {
|
||||
const $ = cheerio.load(dotlinkText);
|
||||
const filepressLink = $(
|
||||
'.btn.btn-sm.btn-outline[style="background:linear-gradient(135deg,rgb(252,185,0) 0%,rgb(0,0,0)); color: #fdf8f2;"]'
|
||||
)
|
||||
.parent()
|
||||
.attr("href");
|
||||
// console.log('filepressLink', filepressLink);
|
||||
const filepressID = filepressLink?.split("/").pop();
|
||||
const filepressBaseUrl = filepressLink
|
||||
?.split("/")
|
||||
.slice(0, -2)
|
||||
.join("/");
|
||||
// console.log('filepressID', filepressID);
|
||||
// console.log('filepressBaseUrl', filepressBaseUrl);
|
||||
const filepressTokenRes = await axios.post(
|
||||
filepressBaseUrl + "/api/file/downlaod/",
|
||||
{
|
||||
id: filepressID,
|
||||
method: "indexDownlaod",
|
||||
captchaValue: null,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Referer: filepressBaseUrl,
|
||||
},
|
||||
}
|
||||
);
|
||||
// console.log('filepressTokenRes', filepressTokenRes.data);
|
||||
if (filepressTokenRes.data?.status) {
|
||||
const filepressToken = filepressTokenRes.data?.data;
|
||||
const filepressStreamLink = await axios.post(
|
||||
filepressBaseUrl + "/api/file/downlaod2/",
|
||||
{
|
||||
id: filepressToken,
|
||||
method: "indexDownlaod",
|
||||
captchaValue: null,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Referer: filepressBaseUrl,
|
||||
},
|
||||
}
|
||||
);
|
||||
// console.log('filepressStreamLink', filepressStreamLink.data);
|
||||
streamLinks.push({
|
||||
server: "filepress",
|
||||
link: filepressStreamLink.data?.data?.[0],
|
||||
type: "mkv",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("filepress error: ");
|
||||
// console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return await hubcloudExtracter(link, signal);
|
||||
} catch (error: any) {
|
||||
console.log("getStream error: ", error);
|
||||
if (error.message.includes("Aborted")) {
|
||||
} else {
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
10
providers/skyMovieHD/catalog.ts
Normal file
10
providers/skyMovieHD/catalog.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const catalog = [
|
||||
{
|
||||
title: "Trending",
|
||||
filter: "",
|
||||
},
|
||||
{
|
||||
title: "JIo-Studios",
|
||||
filter: "category/jio-studios/",
|
||||
},
|
||||
];
|
||||
47
providers/skyMovieHD/episodes.ts
Normal file
47
providers/skyMovieHD/episodes.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { EpisodeLink, ProviderContext } from "../types";
|
||||
|
||||
export const getEpisodes = function ({
|
||||
url,
|
||||
providerContext,
|
||||
}: {
|
||||
url: string;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<EpisodeLink[]> {
|
||||
const { axios, cheerio, commonHeaders: headers } = providerContext;
|
||||
console.log("getEpisodeLinks", url);
|
||||
|
||||
return axios
|
||||
.get(url, { headers })
|
||||
.then((res) => {
|
||||
const $ = cheerio.load(res.data);
|
||||
const container = $(".entry-content, .entry-inner");
|
||||
|
||||
// Remove unnecessary elements
|
||||
$(".unili-content, .code-block-1").remove();
|
||||
|
||||
const episodes: EpisodeLink[] = [];
|
||||
|
||||
container.find("h4, h3").each((_, element) => {
|
||||
const el = $(element);
|
||||
let title = el.text().replace(/[-:]/g, "").trim();
|
||||
if (!title) return;
|
||||
|
||||
// Saare V-Cloud links fetch
|
||||
el.next("p")
|
||||
.find("a[href*='vcloud.lol']")
|
||||
.each((_, a) => {
|
||||
const anchor = $(a);
|
||||
const href = anchor.attr("href")?.trim();
|
||||
if (href) {
|
||||
episodes.push({ title, link: href });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return episodes;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("getEpisodeLinks error:", err);
|
||||
return [];
|
||||
});
|
||||
};
|
||||
268
providers/skyMovieHD/meta.ts
Normal file
268
providers/skyMovieHD/meta.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
import { Info, Link, ProviderContext } from "../types";
|
||||
|
||||
interface DirectLink {
|
||||
link: string;
|
||||
title: string;
|
||||
quality: string;
|
||||
type: "movie" | "episode";
|
||||
}
|
||||
|
||||
interface Episode {
|
||||
title: string;
|
||||
directLinks: DirectLink[];
|
||||
}
|
||||
|
||||
const headers = {
|
||||
Referer: "https://google.com",
|
||||
"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",
|
||||
};
|
||||
|
||||
export async function fetchEpisodesFromSelectedLink(
|
||||
url: string,
|
||||
providerContext: ProviderContext
|
||||
): Promise<Episode[]> {
|
||||
const { axios, cheerio } = providerContext;
|
||||
const res = await axios.get(url, { headers });
|
||||
const $ = cheerio.load(res.data);
|
||||
|
||||
const episodes: Episode[] = [];
|
||||
|
||||
$("h4").each((_, h4El) => {
|
||||
const epTitle = $(h4El).text().trim();
|
||||
if (!epTitle) return;
|
||||
|
||||
const directLinks: DirectLink[] = [];
|
||||
|
||||
$(h4El)
|
||||
.nextUntil("h4, hr")
|
||||
.find("a[href]")
|
||||
.each((_, linkEl) => {
|
||||
let href = ($(linkEl).attr("href") || "").trim();
|
||||
if (!href) return;
|
||||
if (!href.startsWith("http")) href = new URL(href, url).href;
|
||||
|
||||
const btnText = $(linkEl).text().trim() || "Watch Episode";
|
||||
directLinks.push({
|
||||
link: href,
|
||||
title: btnText,
|
||||
quality: "AUTO",
|
||||
type: "episode",
|
||||
});
|
||||
});
|
||||
|
||||
if (directLinks.length > 0) {
|
||||
episodes.push({
|
||||
title: epTitle,
|
||||
directLinks,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
// --- Main getMeta function
|
||||
export const getMeta = async function ({
|
||||
link,
|
||||
providerContext,
|
||||
}: {
|
||||
link: string;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<
|
||||
Info & { extraInfo: Record<string, string>; episodeList: Episode[] }
|
||||
> {
|
||||
const { axios, cheerio } = providerContext;
|
||||
if (!link.startsWith("http"))
|
||||
link = new URL(link, "https://vgmlinks.click").href;
|
||||
|
||||
try {
|
||||
const res = await axios.get(link, { headers });
|
||||
const $ = cheerio.load(res.data);
|
||||
const content = $(".entry-content, .post-inner").length
|
||||
? $(".entry-content, .post-inner")
|
||||
: $("body");
|
||||
|
||||
const title =
|
||||
$("h1.entry-title").first().text().trim() ||
|
||||
$("meta[property='og:title']").attr("content")?.trim() ||
|
||||
"Unknown"; // --- Type Detect ---
|
||||
|
||||
const pageText = content.text();
|
||||
const type =
|
||||
/Season\s*\d+/i.test(pageText) || /Episode\s*\d+/i.test(pageText)
|
||||
? "series"
|
||||
: "movie";
|
||||
|
||||
let image =
|
||||
$(".poster img").attr("src") ||
|
||||
$("meta[property='og:image']").attr("content") ||
|
||||
$("meta[name='twitter:image']").attr("content") ||
|
||||
"";
|
||||
if (image && !image.startsWith("http")) image = new URL(image, link).href;
|
||||
|
||||
let synopsis = "";
|
||||
$(".entry-content p").each((_, el) => {
|
||||
const txt = $(el).text().trim();
|
||||
if (txt.length > 40 && !txt.toLowerCase().includes("download")) {
|
||||
synopsis = txt;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const imdbLink = $("a[href*='imdb.com']").attr("href") || "";
|
||||
const imdbId = imdbLink
|
||||
? "tt" + (imdbLink.split("/tt")[1]?.split("/")[0] || "")
|
||||
: "";
|
||||
|
||||
const tags: string[] = [];
|
||||
$(".entry-content p strong").each((_, el) => {
|
||||
const txt = $(el).text().trim();
|
||||
if (
|
||||
txt.match(
|
||||
/drama|biography|action|thriller|romance|adventure|animation/i
|
||||
)
|
||||
)
|
||||
tags.push(txt);
|
||||
});
|
||||
|
||||
const extra: Record<string, string> = {};
|
||||
$("p").each((_, el) => {
|
||||
const html = $(el).html() || "";
|
||||
if (html.includes("Series Name"))
|
||||
extra.name = $(el).text().split(":")[1]?.trim();
|
||||
if (html.includes("Language"))
|
||||
extra.language = $(el).text().split(":")[1]?.trim();
|
||||
if (html.includes("Released Year"))
|
||||
extra.year = $(el).text().split(":")[1]?.trim();
|
||||
if (html.includes("Quality"))
|
||||
extra.quality = $(el).text().split(":")[1]?.trim();
|
||||
if (html.includes("Episode Size"))
|
||||
extra.size = $(el).text().split(":")[1]?.trim();
|
||||
if (html.includes("Format"))
|
||||
extra.format = $(el).text().split(":")[1]?.trim();
|
||||
});
|
||||
|
||||
const links: Link[] = [];
|
||||
const episodeList: Episode[] = [];
|
||||
|
||||
const isInformationalHeading = (text: string) => {
|
||||
const lowerText = text.toLowerCase();
|
||||
return (
|
||||
lowerText.includes("series info") ||
|
||||
lowerText.includes("series name") ||
|
||||
lowerText.includes("language") ||
|
||||
lowerText.includes("released year") ||
|
||||
lowerText.includes("episode size") ||
|
||||
lowerText.includes("format") ||
|
||||
lowerText.includes("imdb rating") ||
|
||||
lowerText.includes("winding up") ||
|
||||
(lowerText.length < 5 && !/\d/.test(lowerText))
|
||||
);
|
||||
}; // --- Download Links Extraction ---
|
||||
|
||||
if (type === "series") {
|
||||
// Series case: h3 text as title + episode link button (V-Cloud)
|
||||
content.find("h3").each((_, h3) => {
|
||||
const h3Text = $(h3).text().trim();
|
||||
|
||||
if (isInformationalHeading(h3Text)) return;
|
||||
|
||||
const qualityMatch = h3Text.match(/\d+p/)?.[0] || "AUTO";
|
||||
|
||||
const vcloudLink = $(h3)
|
||||
.nextUntil("h3, hr")
|
||||
.find("a")
|
||||
.filter((_, a) => /v-cloud|mega|gdrive|download/i.test($(a).text()))
|
||||
.first();
|
||||
|
||||
const href = vcloudLink.attr("href");
|
||||
if (href) {
|
||||
// Hide unwanted texts
|
||||
const btnText = vcloudLink.text().trim() || "Link";
|
||||
if (
|
||||
btnText.toLowerCase().includes("imdb rating") ||
|
||||
btnText.toLowerCase().includes("winding up")
|
||||
)
|
||||
return;
|
||||
|
||||
links.push({
|
||||
title: h3Text,
|
||||
quality: qualityMatch,
|
||||
episodesLink: href,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Movie case: h5/h3 text as title + direct download link
|
||||
content.find("h3, h5").each((_, heading) => {
|
||||
const headingText = $(heading).text().trim();
|
||||
|
||||
if (isInformationalHeading(headingText)) return;
|
||||
|
||||
const qualityMatch = headingText.match(/\d+p/)?.[0] || "AUTO";
|
||||
const linkEl = $(heading)
|
||||
.nextUntil("h3, h5, hr")
|
||||
.find("a[href]")
|
||||
.first();
|
||||
|
||||
const href = linkEl.attr("href");
|
||||
if (href) {
|
||||
let finalHref = href.trim();
|
||||
if (!finalHref.startsWith("http"))
|
||||
finalHref = new URL(finalHref, link).href;
|
||||
|
||||
const btnText = linkEl.text().trim() || "Download Link"; // Hide unwanted texts
|
||||
|
||||
if (
|
||||
btnText.toLowerCase().includes("imdb rating") ||
|
||||
btnText.toLowerCase().includes("winding up")
|
||||
)
|
||||
return;
|
||||
|
||||
links.push({
|
||||
title: headingText,
|
||||
quality: qualityMatch,
|
||||
episodesLink: "",
|
||||
directLinks: [
|
||||
{
|
||||
title: btnText,
|
||||
link: finalHref,
|
||||
type: "movie",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
synopsis,
|
||||
image,
|
||||
imdbId,
|
||||
type: type as "movie" | "series",
|
||||
tags,
|
||||
cast: [],
|
||||
rating: $(".entry-meta .entry-date").text().trim() || "",
|
||||
linkList: links,
|
||||
extraInfo: extra,
|
||||
episodeList,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("getMeta error:", err);
|
||||
return {
|
||||
title: "",
|
||||
synopsis: "",
|
||||
image: "",
|
||||
imdbId: "",
|
||||
type: "movie",
|
||||
tags: [],
|
||||
cast: [],
|
||||
rating: "",
|
||||
linkList: [],
|
||||
extraInfo: {},
|
||||
episodeList: [],
|
||||
};
|
||||
}
|
||||
};
|
||||
118
providers/skyMovieHD/posts.ts
Normal file
118
providers/skyMovieHD/posts.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
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<Post[]> {
|
||||
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<Post[]> {
|
||||
return fetchPosts({ filter: "", page, query: searchQuery, signal, providerContext });
|
||||
}
|
||||
|
||||
// --- Core fetch function ---
|
||||
async function fetchPosts({
|
||||
filter,
|
||||
query,
|
||||
page = 1,
|
||||
signal,
|
||||
providerContext,
|
||||
}: {
|
||||
filter?: string;
|
||||
query?: string;
|
||||
page?: number;
|
||||
signal?: AbortSignal;
|
||||
providerContext: ProviderContext;
|
||||
}): Promise<Post[]> {
|
||||
try {
|
||||
const baseUrl = "https://skymovieshd.tattoo";
|
||||
let url: string;
|
||||
|
||||
if (query && query.trim() && query.trim().toLowerCase() !== "what are you looking for?") {
|
||||
const params = new URLSearchParams();
|
||||
params.append("s", query.trim());
|
||||
if (page > 1) params.append("paged", page.toString());
|
||||
url = `${baseUrl}/?${params.toString()}`;
|
||||
} 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<string>();
|
||||
const catalog: Post[] = [];
|
||||
|
||||
// ✅ Scrape posts
|
||||
$("article.latestpost").each((_, el) => {
|
||||
const card = $(el);
|
||||
|
||||
// Link
|
||||
let link = card.find("header.entry-header h2.entry-title a, header.entry-header h1.entry-title a").attr("href") || "";
|
||||
if (!link) return;
|
||||
link = resolveUrl(link);
|
||||
if (seen.has(link)) return;
|
||||
|
||||
// Title: remove "Download"
|
||||
let title = card.find("header.entry-header h2.entry-title a, header.entry-header h1.entry-title a")
|
||||
.text()
|
||||
.replace(/^Download\s*/i, "")
|
||||
.trim();
|
||||
if (!title) return;
|
||||
|
||||
// Image
|
||||
let img =
|
||||
card.find("a#featured-thumbnail img").attr("data-src") ||
|
||||
card.find("a#featured-thumbnail img").attr("src") ||
|
||||
"";
|
||||
const image = img ? resolveUrl(img) : "";
|
||||
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
49
providers/skyMovieHD/stream.ts
Normal file
49
providers/skyMovieHD/stream.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
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, extractors } = providerContext;
|
||||
const { hubcloudExtracter } = extractors;
|
||||
try {
|
||||
const streamLinks: Stream[] = [];
|
||||
console.log("dotlink", link);
|
||||
|
||||
return await hubcloudExtracter(link, signal);
|
||||
} catch (error: any) {
|
||||
console.log("getStream error: ", error);
|
||||
if (error.message.includes("Aborted")) {
|
||||
} else {
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user