Compare commits

8 Commits

Author SHA1 Message Date
Himanshu 3d90046acd fix: bump animetsu version 2026-06-19 20:37:16 +05:30
Himanshu d82c9aeea4 fix: remove animetsu stream link proxy 2026-06-19 20:36:47 +05:30
Himanshu 8e6d16c7b6 fix: bump animetsu version 2026-06-19 19:27:35 +05:30
Himanshu 0a2d6faf21 fix: proxy animetsu stream links 2026-06-19 19:26:01 +05:30
Himanshu c671054045 fix: update animetsu stream logic and bump version 2026-06-19 17:49:44 +05:30
Himanshu cb36604e9a fix: update animetsu provider API endpoints 2026-06-19 13:32:06 +05:30
Himanshu 695dd89f4e fix: update animetsu and movies4u providers 2026-06-19 13:10:37 +05:30
Himanshu 3fedeb8816 feat: add openWebView to ProviderContext 2026-06-19 13:10:25 +05:30
12 changed files with 337 additions and 178 deletions
+1 -1
View File
@@ -1 +1 @@
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),catalog_exports={};__export(catalog_exports,{catalog:()=>catalog,genres:()=>genres});var catalog=[{title:"Popular",filter:"/api/anime/search?query=&page=1&perPage=35&year=any&sort=favourites&season=any&format=any&status=any"},{title:"Trending",filter:"/api/anime/search?query=&page=1&perPage=35&year=any&sort=trending&season=any&format=any&status=any"},{title:"Top Rated",filter:"/api/anime/search?query=&page=1&perPage=35&year=any&sort=rating&season=any&format=any&status=any"},{title:"Favourites",filter:"/api/anime/search?query=&page=1&perPage=35&year=any&sort=updated&season=any&format=any&status=any"}],genres=[];exports.catalog=catalog,exports.genres=genres; "use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),catalog_exports={};__export(catalog_exports,{catalog:()=>catalog,genres:()=>genres});var catalog=[{title:"Popular",filter:"popular"},{title:"Trending",filter:"trending"},{title:"Top Rated",filter:"top"},{title:"Seasonal",filter:"seasonal"}],genres=[];exports.catalog=catalog,exports.genres=genres;
+1 -1
View File
@@ -1 +1 @@
"use strict";var __defProp=Object.defineProperty,__defProps=Object.defineProperties,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropDescs=Object.getOwnPropertyDescriptors,__getOwnPropNames=Object.getOwnPropertyNames,__getOwnPropSymbols=Object.getOwnPropertySymbols,__hasOwnProp=Object.prototype.hasOwnProperty,__propIsEnum=Object.prototype.propertyIsEnumerable,__defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value:value}):obj[key]=value,__spreadValues=(a,b)=>{for(var prop in b||(b={}))__hasOwnProp.call(b,prop)&&__defNormalProp(a,prop,b[prop]);if(__getOwnPropSymbols)for(var prop of __getOwnPropSymbols(b))__propIsEnum.call(b,prop)&&__defNormalProp(a,prop,b[prop]);return a},__spreadProps=(a,b)=>__defProps(a,__getOwnPropDescs(b)),__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),meta_exports={};__export(meta_exports,{getMeta:()=>getMeta});var getMeta=__name(function(_0){return __async(this,arguments,function*({link:link,providerContext:providerContext}){var _a,_b,_c,_d,_e,_f;try{const{axios:axios}=providerContext,baseUrl="https://backend.animetsu.to",url=`${baseUrl}/api/anime/info/${link}`,data=(yield axios.get(url,{headers:{Referer:"https://animetsu.to/"}})).data,meta={title:(null==(_a=data.title)?void 0:_a.english)||(null==(_b=data.title)?void 0:_b.romaji)||(null==(_c=data.title)?void 0:_c.native)||"",synopsis:data.description||"",image:(null==(_d=data.coverImage)?void 0:_d.extraLarge)||(null==(_e=data.coverImage)?void 0:_e.large)||(null==(_f=data.coverImage)?void 0:_f.medium)||"",tags:[null==data?void 0:data.format,null==data?void 0:data.status,...(null==data?void 0:data.genres)||[]].filter(Boolean),imdbId:"",type:"MOVIE"===data.format?"movie":"series"},linkList=[];try{const episodes=(yield axios.get(`${baseUrl}/api/anime/eps/${link}`,{headers:{Referer:"https://animetsu.to/"}})).data;if(episodes&&episodes.length>0){const directLinks=[];episodes.forEach(episode=>{const title=`Episode ${episode.number}`,episodeLink=`${link}:${episode.number}`;episodeLink&&title&&directLinks.push({title:title,link:episodeLink})}),linkList.push({title:meta.title,directLinks:directLinks})}else linkList.push({title:meta.title,directLinks:[{title:"Movie",link:`${link}:1`}]})}catch(episodeErr){console.error("Error fetching episodes:",episodeErr),linkList.push({title:meta.title,directLinks:[{title:meta.title,link:`${link}:1`}]})}return __spreadProps(__spreadValues({},meta),{linkList:linkList})}catch(err){return console.error("animetsu meta error:",err),{title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]}}})},"getMeta");exports.getMeta=getMeta; "use strict";var __defProp=Object.defineProperty,__defProps=Object.defineProperties,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropDescs=Object.getOwnPropertyDescriptors,__getOwnPropNames=Object.getOwnPropertyNames,__getOwnPropSymbols=Object.getOwnPropertySymbols,__hasOwnProp=Object.prototype.hasOwnProperty,__propIsEnum=Object.prototype.propertyIsEnumerable,__defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value:value}):obj[key]=value,__spreadValues=(a,b)=>{for(var prop in b||(b={}))__hasOwnProp.call(b,prop)&&__defNormalProp(a,prop,b[prop]);if(__getOwnPropSymbols)for(var prop of __getOwnPropSymbols(b))__propIsEnum.call(b,prop)&&__defNormalProp(a,prop,b[prop]);return a},__spreadProps=(a,b)=>__defProps(a,__getOwnPropDescs(b)),__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),meta_exports={};__export(meta_exports,{getMeta:()=>getMeta});var getMeta=__name(function(_0){return __async(this,arguments,function*({link:link,providerContext:providerContext}){var _a,_b,_c,_d,_e,_f,_g;try{const{axios:axios,openWebView:openWebView,commonHeaders:commonHeaders}=providerContext,baseUrl="https://animetsu.net",url=`${baseUrl}/v2/api/anime/info/${link}`;let cookies,res;try{res=yield axios.get(url,{headers:__spreadProps(__spreadValues({},commonHeaders),{Referer:baseUrl})})}catch(error){if(403!==(null==(_a=error.response)?void 0:_a.status))throw error;{const wafResult=yield openWebView(baseUrl,{title:"Solve the captcha below and click done",description:"Required to bypass Animetsu anti-bot protection.",headers:__spreadProps(__spreadValues({},commonHeaders),{Referer:baseUrl}),force:!0,waitForCookie:"cf_clearance"});cookies=wafResult.cookies,res=yield axios.get(url,{headers:__spreadProps(__spreadValues({},commonHeaders),{Referer:baseUrl,Cookie:cookies})})}}const data=res.data,meta={title:(null==(_b=data.title)?void 0:_b.english)||(null==(_c=data.title)?void 0:_c.romaji)||(null==(_d=data.title)?void 0:_d.native)||"",synopsis:data.description||"",image:(null==(_e=data.cover_image)?void 0:_e.large)||(null==(_f=data.cover_image)?void 0:_f.medium)||(null==(_g=data.cover_image)?void 0:_g.small)||"",tags:[null==data?void 0:data.format,null==data?void 0:data.status,...(null==data?void 0:data.genres)||[]].filter(Boolean),imdbId:"",type:"MOVIE"===data.format?"movie":"series"},linkList=[],seasons=data.seasons;if(seasons&&seasons.length>0)yield Promise.all(seasons.map(season=>__async(null,null,function*(){var _a2,_b2,_c2;const seasonTitle=(null==(_a2=season.title)?void 0:_a2.english)||(null==(_b2=season.title)?void 0:_b2.romaji)||(null==(_c2=season.title)?void 0:_c2.native),directLinks=[];try{const episodes=(yield axios.get(`${baseUrl}/v2/api/anime/eps/${season.id}`,{headers:__spreadValues(__spreadProps(__spreadValues({},commonHeaders),{Referer:baseUrl}),cookies?{Cookie:cookies}:{})})).data;episodes&&episodes.length>0&&episodes.forEach(ep=>{directLinks.push({title:`Episode ${ep.ep_num}`,link:`${season.id}:${ep.ep_num}`})})}catch(e){const total=season.total_eps||1;for(let i=1;i<=total;i++)directLinks.push({title:`Episode ${i}`,link:`${season.id}:${i}`})}directLinks.length>0&&linkList.push({title:seasonTitle||meta.title,directLinks:directLinks})})));else{const total=data.total_eps||1,directLinks=[];for(let i=1;i<=total;i++)directLinks.push({title:1===total?"Movie":`Episode ${i}`,link:`${link}:${i}`});linkList.push({title:meta.title,directLinks:directLinks})}return __spreadProps(__spreadValues({},meta),{linkList:linkList})}catch(err){return console.error("animetsu meta error:",err),{title:"",synopsis:"",image:"",imdbId:"",type:"movie",linkList:[]}}})},"getMeta");exports.getMeta=getMeta;
+1 -1
View File
@@ -1 +1 @@
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),posts_exports={};__export(posts_exports,{getPosts:()=>getPosts,getSearchPosts:()=>getSearchPosts});var getPosts=__name(function(_0){return __async(this,arguments,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){const{axios:axios}=providerContext,url="https://backend.animetsu.to"+filter+"&page="+page.toString();return console.log("animetsuGetPosts url",url),posts({url:url.toString(),signal:signal,axios:axios})})},"getPosts"),getSearchPosts=__name(function(_0){return __async(this,arguments,function*({searchQuery:searchQuery,page:page,signal:signal,providerContext:providerContext}){const{axios:axios}=providerContext;return posts({url:`https://backend.animetsu.to/api/anime/search?query=${encodeURIComponent(searchQuery)}&page=${page}&perPage=35&year=any&sort=favourites&season=any&format=any&status=any`,signal:signal,axios:axios})})},"getSearchPosts");function posts(_0){return __async(this,arguments,function*({url:url,signal:signal,axios:axios}){var _a;try{const data=null==(_a=(yield axios.get(url,{signal:signal,headers:{Referer:"https://animetsu.to/"}})).data)?void 0:_a.results,catalog=[];return null==data||data.map(element=>{var _a2,_b,_c,_d,_e,_f,_g;const title=(null==(_a2=element.title)?void 0:_a2.english)||(null==(_b=element.title)?void 0:_b.romaji)||(null==(_c=element.title)?void 0:_c.native),link=null==(_d=element.id)?void 0:_d.toString(),image=(null==(_e=element.coverImage)?void 0:_e.large)||(null==(_f=element.coverImage)?void 0:_f.extraLarge)||(null==(_g=element.coverImage)?void 0:_g.medium);title&&link&&image&&catalog.push({title:title,link:link,image:image})}),catalog}catch(err){return console.error("animetsu error ",err),[]}})}__name(posts,"posts"),exports.getPosts=getPosts,exports.getSearchPosts=getSearchPosts; "use strict";var __defProp=Object.defineProperty,__defProps=Object.defineProperties,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropDescs=Object.getOwnPropertyDescriptors,__getOwnPropNames=Object.getOwnPropertyNames,__getOwnPropSymbols=Object.getOwnPropertySymbols,__hasOwnProp=Object.prototype.hasOwnProperty,__propIsEnum=Object.prototype.propertyIsEnumerable,__defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:!0,configurable:!0,writable:!0,value:value}):obj[key]=value,__spreadValues=(a,b)=>{for(var prop in b||(b={}))__hasOwnProp.call(b,prop)&&__defNormalProp(a,prop,b[prop]);if(__getOwnPropSymbols)for(var prop of __getOwnPropSymbols(b))__propIsEnum.call(b,prop)&&__defNormalProp(a,prop,b[prop]);return a},__spreadProps=(a,b)=>__defProps(a,__getOwnPropDescs(b)),__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),posts_exports={};__export(posts_exports,{getPosts:()=>getPosts,getSearchPosts:()=>getSearchPosts});var getPosts=__name(function(_0){return __async(this,arguments,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){if(page>1)return[];const{axios:axios,commonHeaders:commonHeaders}=providerContext;return posts({url:"https://animetsu.net/v2/api/anime/home",filter:filter,signal:signal,axios:axios,providerContext:providerContext,headers:commonHeaders})})},"getPosts"),getSearchPosts=__name(function(_0){return __async(this,arguments,function*({searchQuery:searchQuery,page:page,signal:signal,providerContext:providerContext}){const{axios:axios,commonHeaders:commonHeaders}=providerContext;return posts({url:`https://animetsu.net/v2/api/anime/search/?query=${encodeURIComponent(searchQuery)}`,signal:signal,axios:axios,providerContext:providerContext,headers:commonHeaders})})},"getSearchPosts");function posts(_0){return __async(this,arguments,function*({url:url,filter:filter,signal:signal,axios:axios,providerContext:providerContext,headers:headers}){var _a,_b,_c;const baseUrl="https://animetsu.net",{openWebView:openWebView}=providerContext;try{let cookies,res;try{res=yield axios.get(url,{signal:signal,headers:__spreadProps(__spreadValues({},headers),{Referer:baseUrl})})}catch(error){if(403!==(null==(_a=error.response)?void 0:_a.status))throw error;cookies=(yield openWebView(baseUrl,{title:"Solve the captcha below and click done",description:"Required to bypass Animetsu anti-bot protection.",headers:__spreadProps(__spreadValues({},headers),{Referer:baseUrl}),force:!0,waitForCookie:"cf_clearance"})).cookies,res=yield axios.get(url,{signal:signal,headers:__spreadProps(__spreadValues({},headers),{Referer:baseUrl,Cookie:cookies})})}const data=filter?null==(_b=res.data)?void 0:_b[filter]:(null==(_c=res.data)?void 0:_c.results)||res.data,catalog=[];return null==data||data.map(element=>{var _a2,_b2,_c2,_d,_e,_f,_g,_h,_i,_j,_k;const title=(null==(_a2=element.title)?void 0:_a2.english)||(null==(_b2=element.title)?void 0:_b2.romaji)||(null==(_c2=element.title)?void 0:_c2.native),link=null==(_d=element.id)?void 0:_d.toString(),image=(null==(_e=element.cover_image)?void 0:_e.large)||(null==(_f=element.cover_image)?void 0:_f.extraLarge)||(null==(_g=element.cover_image)?void 0:_g.medium)||(null==(_h=element.cover_image)?void 0:_h.small)||(null==(_i=element.coverImage)?void 0:_i.large)||(null==(_j=element.coverImage)?void 0:_j.extraLarge)||(null==(_k=element.coverImage)?void 0:_k.medium);title&&link&&image&&catalog.push({title:title,link:link,image:image})}),catalog}catch(err){return console.error("animetsu error ",err),[]}})}__name(posts,"posts"),exports.getPosts=getPosts,exports.getSearchPosts=getSearchPosts;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -114,7 +114,7 @@
{ {
"display_name": "Movies4U", "display_name": "Movies4U",
"value": "movies4u", "value": "movies4u",
"version": "1.6", "version": "1.7",
"icon": "", "icon": "",
"type": "global", "type": "global",
"disabled": false "disabled": false
@@ -258,10 +258,10 @@
{ {
"display_name": "Animetsu", "display_name": "Animetsu",
"value": "animetsu", "value": "animetsu",
"version": "1.1", "version": "1.7",
"icon": "", "icon": "",
"type": "english", "type": "english",
"disabled": true "disabled": false
}, },
{ {
"display_name": "TokyoInsider", "display_name": "TokyoInsider",
+5 -9
View File
@@ -1,23 +1,19 @@
export const catalog = [ export const catalog = [
{ {
title: "Popular", title: "Popular",
filter: filter: "popular",
"/api/anime/search?query=&page=1&perPage=35&year=any&sort=favourites&season=any&format=any&status=any",
}, },
{ {
title: "Trending", title: "Trending",
filter: filter: "trending",
"/api/anime/search?query=&page=1&perPage=35&year=any&sort=trending&season=any&format=any&status=any",
}, },
{ {
title: "Top Rated", title: "Top Rated",
filter: filter: "top",
"/api/anime/search?query=&page=1&perPage=35&year=any&sort=rating&season=any&format=any&status=any",
}, },
{ {
title: "Favourites", title: "Seasonal",
filter: filter: "seasonal",
"/api/anime/search?query=&page=1&perPage=35&year=any&sort=updated&season=any&format=any&status=any",
}, },
]; ];
+78 -52
View File
@@ -8,15 +8,33 @@ export const getMeta = async function ({
providerContext: ProviderContext; providerContext: ProviderContext;
}): Promise<Info> { }): Promise<Info> {
try { try {
const { axios } = providerContext; const { axios, openWebView, commonHeaders } = providerContext;
const baseUrl = "https://backend.animetsu.to"; const baseUrl = "https://animetsu.net";
const url = `${baseUrl}/api/anime/info/${link}`; const url = `${baseUrl}/v2/api/anime/info/${link}`;
const res = await axios.get(url, { let cookies: string | undefined;
headers: { let res: any;
Referer: "https://animetsu.to/", try {
}, res = await axios.get(url, {
headers: { ...commonHeaders, Referer: baseUrl },
}); });
} catch (error: any) {
if (error.response?.status === 403) {
const wafResult = await openWebView(baseUrl, {
title: "Solve the captcha below and click done",
description: "Required to bypass Animetsu anti-bot protection.",
headers: { ...commonHeaders, Referer: baseUrl },
force: true,
waitForCookie: "cf_clearance",
});
cookies = wafResult.cookies;
res = await axios.get(url, {
headers: { ...commonHeaders, Referer: baseUrl, Cookie: cookies },
});
} else {
throw error;
}
}
const data = res.data; const data = res.data;
const meta = { const meta = {
@@ -24,12 +42,12 @@ export const getMeta = async function ({
data.title?.english || data.title?.romaji || data.title?.native || "", data.title?.english || data.title?.romaji || data.title?.native || "",
synopsis: data.description || "", synopsis: data.description || "",
image: image:
data.coverImage?.extraLarge || data.cover_image?.large ||
data.coverImage?.large || data.cover_image?.medium ||
data.coverImage?.medium || data.cover_image?.small ||
"", "",
tags: [data?.format, data?.status, ...(data?.genres || [])].filter( tags: [data?.format, data?.status, ...(data?.genres || [])].filter(
Boolean Boolean,
), ),
imdbId: "", imdbId: "",
type: data.format === "MOVIE" ? "movie" : "series", type: data.format === "MOVIE" ? "movie" : "series",
@@ -37,59 +55,67 @@ export const getMeta = async function ({
const linkList: Link[] = []; const linkList: Link[] = [];
// Get episodes data const seasons = data.seasons;
try { if (seasons && seasons.length > 0) {
const episodesRes = await axios.get(`${baseUrl}/api/anime/eps/${link}`, { await Promise.all(
headers: { seasons.map(async (season: any) => {
Referer: "https://animetsu.to/", const seasonTitle =
}, season.title?.english ||
}); season.title?.romaji ||
const episodes = episodesRes.data; season.title?.native;
if (episodes && episodes.length > 0) {
const directLinks: Link["directLinks"] = []; const directLinks: Link["directLinks"] = [];
episodes.forEach((episode: any) => { try {
const title = `Episode ${episode.number}`; const epsRes = await axios.get(
const episodeLink = `${link}:${episode.number}`; `${baseUrl}/v2/api/anime/eps/${season.id}`,
{
if (episodeLink && title) { headers: {
...commonHeaders,
Referer: baseUrl,
...(cookies ? { Cookie: cookies } : {}),
},
},
);
const episodes = epsRes.data;
if (episodes && episodes.length > 0) {
episodes.forEach((ep: any) => {
directLinks.push({ directLinks.push({
title, title: `Episode ${ep.ep_num}`,
link: episodeLink, link: `${season.id}:${ep.ep_num}`,
});
}); });
} }
} catch {
// fallback: use total_eps count
const total = season.total_eps || 1;
for (let i = 1; i <= total; i++) {
directLinks.push({
title: `Episode ${i}`,
link: `${season.id}:${i}`,
}); });
}
}
if (directLinks.length > 0) {
linkList.push({ linkList.push({
title: meta.title, title: seasonTitle || meta.title,
directLinks: directLinks, directLinks,
});
} else {
// Movie case - single episode
linkList.push({
title: meta.title,
directLinks: [
{
title: "Movie",
link: `${link}:1`,
},
],
}); });
} }
} catch (episodeErr) { }),
console.error("Error fetching episodes:", episodeErr); );
// Fallback for movie or single episode } else {
linkList.push({ // Movie or single-season fallback
title: meta.title, const total = data.total_eps || 1;
directLinks: [ const directLinks: Link["directLinks"] = [];
{ for (let i = 1; i <= total; i++) {
title: meta.title, directLinks.push({
link: `${link}:1`, title: total === 1 ? "Movie" : `Episode ${i}`,
}, link: `${link}:${i}`,
],
}); });
} }
linkList.push({ title: meta.title, directLinks });
}
return { return {
...meta, ...meta,
+57 -16
View File
@@ -12,14 +12,21 @@ export const getPosts = async function ({
signal: AbortSignal; signal: AbortSignal;
providerContext: ProviderContext; providerContext: ProviderContext;
}): Promise<Post[]> { }): Promise<Post[]> {
const { axios } = providerContext; if (page > 1) {
const baseUrl = "https://backend.animetsu.to"; return [];
}
const { axios, commonHeaders } = providerContext;
const baseUrl = "https://animetsu.net";
const url = `${baseUrl}/v2/api/anime/home`;
// Parse filter to modify page parameter return posts({
const url = baseUrl + filter + "&page=" + page.toString(); url,
console.log("animetsuGetPosts url", url); filter,
signal,
return posts({ url: url.toString(), signal, axios }); axios,
providerContext,
headers: commonHeaders,
});
}; };
export const getSearchPosts = async function ({ export const getSearchPosts = async function ({
@@ -34,32 +41,62 @@ export const getSearchPosts = async function ({
signal: AbortSignal; signal: AbortSignal;
providerContext: ProviderContext; providerContext: ProviderContext;
}): Promise<Post[]> { }): Promise<Post[]> {
const { axios } = providerContext; const { axios, commonHeaders } = providerContext;
const baseUrl = "https://backend.animetsu.to"; const baseUrl = "https://animetsu.net";
const url = `${baseUrl}/api/anime/search?query=${encodeURIComponent( const url = `${baseUrl}/v2/api/anime/search/?query=${encodeURIComponent(
searchQuery searchQuery,
)}&page=${page}&perPage=35&year=any&sort=favourites&season=any&format=any&status=any`; )}`;
return posts({ url, signal, axios }); return posts({ url, signal, axios, providerContext, headers: commonHeaders });
}; };
async function posts({ async function posts({
url, url,
filter,
signal, signal,
axios, axios,
providerContext,
headers,
}: { }: {
url: string; url: string;
filter?: string;
signal: AbortSignal; signal: AbortSignal;
axios: ProviderContext["axios"]; axios: ProviderContext["axios"];
providerContext: ProviderContext;
headers?: Record<string, string>;
}): Promise<Post[]> { }): Promise<Post[]> {
const baseUrl = "https://animetsu.net";
const { openWebView } = providerContext;
try { try {
const res = await axios.get(url, { let cookies: string | undefined;
let res: any;
try {
res = await axios.get(url, {
signal, signal,
headers: { headers: {
Referer: "https://animetsu.to/", ...headers,
Referer: baseUrl,
}, },
}); });
const data = res.data?.results; } catch (error: any) {
if (error.response?.status === 403) {
const wafResult = await openWebView(baseUrl, {
title: "Solve the captcha below and click done",
description: "Required to bypass Animetsu anti-bot protection.",
headers: { ...headers, Referer: baseUrl },
force: true,
waitForCookie: "cf_clearance",
});
cookies = wafResult.cookies;
res = await axios.get(url, {
signal,
headers: { ...headers, Referer: baseUrl, Cookie: cookies },
});
} else {
throw error;
}
}
const data = filter ? res.data?.[filter] : res.data?.results || res.data;
const catalog: Post[] = []; const catalog: Post[] = [];
data?.map((element: any) => { data?.map((element: any) => {
@@ -69,6 +106,10 @@ async function posts({
element.title?.native; element.title?.native;
const link = element.id?.toString(); const link = element.id?.toString();
const image = const image =
element.cover_image?.large ||
element.cover_image?.extraLarge ||
element.cover_image?.medium ||
element.cover_image?.small ||
element.coverImage?.large || element.coverImage?.large ||
element.coverImage?.extraLarge || element.coverImage?.extraLarge ||
element.coverImage?.medium; element.coverImage?.medium;
+102 -74
View File
@@ -8,8 +8,27 @@ export const getStream = async function ({
providerContext: ProviderContext; providerContext: ProviderContext;
}): Promise<Stream[]> { }): Promise<Stream[]> {
try { try {
const { axios } = providerContext; const { axios, openWebView, commonHeaders } = providerContext;
const baseUrl = "https://backend.animetsu.to"; const baseUrl = "https://animetsu.net";
const streamUrl = `https://swiftstream.top/proxy`;
let wafCookies: string | undefined;
try {
await axios.get(baseUrl, {
headers: { ...commonHeaders, Referer: baseUrl },
});
} catch (error: any) {
if (error.response?.status === 403) {
const wafResult = await openWebView(baseUrl, {
title: "Solve the captcha below and click done",
description: "Required to bypass Animetsu anti-bot protection.",
headers: { ...commonHeaders, Referer: baseUrl },
force: true,
waitForCookie: "cf_clearance",
});
wafCookies = wafResult.cookies;
}
}
// Parse link format: "animeId:episodeNumber" // Parse link format: "animeId:episodeNumber"
const [animeId, episodeNumber] = id.split(":"); const [animeId, episodeNumber] = id.split(":");
@@ -18,61 +37,65 @@ export const getStream = async function ({
throw new Error("Invalid link format"); throw new Error("Invalid link format");
} }
const servers = ["pahe", "zoro"]; // Available servers based on API structure const servers = ["sage", "meg", "dio", "kite"];
const streamLinks: Stream[] = []; const streamLinks: Stream[] = [];
await Promise.all( await Promise.all(
servers.map(async (server) => { servers.map(async (server) => {
try { try {
const url = `${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=sub`; const url = `${baseUrl}/v2/api/anime/oppai/${animeId}/${episodeNumber}?server=${server}&source_type=sub`;
const res = await axios.get(url, { const res = await axios.get(url, {
headers: { headers: {
Referer: "https://animetsu.to/", ...commonHeaders,
Referer: baseUrl,
...(wafCookies ? { Cookie: wafCookies } : {}),
}, },
}); });
if (res.data && res.data.sources) { if (res.data && res.data.sources) {
const subtitles: TextTracks = []; const subtitles: TextTracks = [];
// if (res.data.subtitles && Array.isArray(res.data.subtitles)) { if (res.data.subs && Array.isArray(res.data.subs)) {
// res.data.subtitles.forEach((sub: any) => { res.data.subs.forEach((sub: any) => {
// if (sub.url && sub.lang) { if (sub.url && sub.lang) {
// // Extract language code from lang string (e.g., "English" -> "en", "Arabic - CR" -> "ar") const langCode = sub.lang.toLowerCase().includes("english")
// const langCode = sub.lang.toLowerCase().includes("english") ? "en"
// ? "en" : sub.lang.toLowerCase().includes("arabic")
// : sub.lang.toLowerCase().includes("arabic") ? "ar"
// ? "ar" : sub.lang.toLowerCase().includes("french")
// : sub.lang.toLowerCase().includes("french") ? "fr"
// ? "fr" : sub.lang.toLowerCase().includes("german")
// : sub.lang.toLowerCase().includes("german") ? "de"
// ? "de" : sub.lang.toLowerCase().includes("italian")
// : sub.lang.toLowerCase().includes("italian") ? "it"
// ? "it" : sub.lang.toLowerCase().includes("portuguese")
// : sub.lang.toLowerCase().includes("portuguese") ? "pt"
// ? "pt" : sub.lang.toLowerCase().includes("russian")
// : sub.lang.toLowerCase().includes("russian") ? "ru"
// ? "ru" : sub.lang.toLowerCase().includes("spanish")
// : sub.lang.toLowerCase().includes("spanish") ? "es"
// ? "es" : "und";
// : "und";
// subtitles.push({ subtitles.push({
// title: sub.lang, title: sub.lang,
// language: langCode, language: langCode,
// type: "text/vtt", type: "text/vtt",
// uri: sub.url, uri: sub.url,
// }); });
// } }
// }); });
// } }
res.data.sources.forEach((source: any) => { res.data.sources.forEach((source: any) => {
const sourceUrl = source.url.startsWith("/")
? `${streamUrl}${source.url}`
: source.url;
streamLinks.push({ streamLinks.push({
server: server + `: ${source.quality}`, server: `${server} (Sub): ${source.quality}`,
link: `https://m3u8.8man.workers.dev?url=${source.url}`, link: sourceUrl,
type: "m3u8", type: "m3u8",
quality: source.quality, quality: source.quality,
headers: { headers: {
referer: "https://animetsu.to/", referer: baseUrl,
}, },
subtitles: subtitles.length > 0 ? subtitles : [], subtitles: subtitles.length > 0 ? subtitles : [],
}); });
@@ -81,62 +104,67 @@ export const getStream = async function ({
} catch (e) { } catch (e) {
console.log(`Error with server ${server}:`, e); console.log(`Error with server ${server}:`, e);
} }
}) }),
); );
// Try dub version as well // Try dub version as well
await Promise.all( await Promise.all(
servers.map(async (server) => { servers.map(async (server) => {
try { try {
const url = `${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=dub`; const url = `${baseUrl}/v2/api/anime/oppai/${animeId}/${episodeNumber}?server=${server}&source_type=dub`;
const res = await axios.get(url, { const res = await axios.get(url, {
headers: { headers: {
referer: "https://animetsu.to/", ...commonHeaders,
Referer: baseUrl,
...(wafCookies ? { Cookie: wafCookies } : {}),
}, },
}); });
if (res.data && res.data.sources) { if (res.data && res.data.sources) {
const subtitles: TextTracks = []; const subtitles: TextTracks = [];
// if (res.data.subtitles && Array.isArray(res.data.subtitles)) { if (res.data.subs && Array.isArray(res.data.subs)) {
// res.data.subtitles.forEach((sub: any) => { res.data.subs.forEach((sub: any) => {
// if (sub.url && sub.lang) { if (sub.url && sub.lang) {
// // Extract language code from lang string (e.g., "English" -> "en", "Arabic - CR" -> "ar") // Extract language code from lang string (e.g., "English" -> "en", "Arabic - CR" -> "ar")
// const langCode = sub.lang.toLowerCase().includes("english") const langCode = sub.lang.toLowerCase().includes("english")
// ? "en" ? "en"
// : sub.lang.toLowerCase().includes("arabic") : sub.lang.toLowerCase().includes("arabic")
// ? "ar" ? "ar"
// : sub.lang.toLowerCase().includes("french") : sub.lang.toLowerCase().includes("french")
// ? "fr" ? "fr"
// : sub.lang.toLowerCase().includes("german") : sub.lang.toLowerCase().includes("german")
// ? "de" ? "de"
// : sub.lang.toLowerCase().includes("italian") : sub.lang.toLowerCase().includes("italian")
// ? "it" ? "it"
// : sub.lang.toLowerCase().includes("portuguese") : sub.lang.toLowerCase().includes("portuguese")
// ? "pt" ? "pt"
// : sub.lang.toLowerCase().includes("russian") : sub.lang.toLowerCase().includes("russian")
// ? "ru" ? "ru"
// : sub.lang.toLowerCase().includes("spanish") : sub.lang.toLowerCase().includes("spanish")
// ? "es" ? "es"
// : "und"; : "und";
// subtitles.push({ subtitles.push({
// title: sub.lang, title: sub.lang,
// language: langCode, language: langCode,
// type: "text/vtt", type: "text/vtt",
// uri: sub.url, uri: sub.url,
// }); });
// } }
// }); });
// } }
res.data.sources.forEach((source: any) => { res.data.sources.forEach((source: any) => {
const sourceUrl = source.url.startsWith("/")
? `${streamUrl}${source.url}`
: source.url;
streamLinks.push({ streamLinks.push({
server: `${server} (Dub): ${source.quality}`, server: `${server} (Dub): ${source.quality}`,
link: `https://m3u8.8man.workers.dev?url=${source.url}`, link: sourceUrl,
type: "m3u8", type: "m3u8",
quality: source.quality, quality: source.quality,
headers: { headers: {
referer: "https://animetsu.to/", referer: baseUrl,
}, },
subtitles: subtitles.length > 0 ? subtitles : [], subtitles: subtitles.length > 0 ? subtitles : [],
}); });
@@ -145,7 +173,7 @@ export const getStream = async function ({
} catch (e) { } catch (e) {
console.log(`Error with server ${server} (dub):`, e); console.log(`Error with server ${server} (dub):`, e);
} }
}) }),
); );
console.log("Stream links:", streamLinks); console.log("Stream links:", streamLinks);
+40 -8
View File
@@ -15,11 +15,6 @@ const headers = {
"Sec-Fetch-Mode": "navigate", "Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none", "Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1", "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({ export async function getStream({
@@ -33,13 +28,50 @@ export async function getStream({
signal: AbortSignal; signal: AbortSignal;
providerContext: ProviderContext; providerContext: ProviderContext;
}) { }) {
const { axios, cheerio, commonHeaders } = providerContext; const { axios, cheerio, commonHeaders, openWebView } = providerContext;
try { try {
const streamLinks: Stream[] = []; const streamLinks: Stream[] = [];
console.log("dotlink", link); console.log("dotlink", link);
if (type === "movie") { if (type === "movie") {
// vlink let dotlinkRes;
const dotlinkRes = await axios(`${link}`, { headers }); let cookies: string | undefined;
try {
dotlinkRes = await axios(`${link}`, {
headers: {
...commonHeaders,
Referer: link,
},
});
} catch (error: any) {
if (error.response?.status === 403) {
console.log("Solving WAF for Movies4U...");
const wafResult = await openWebView(link, {
title: "Solve the captcha below and click done",
description:
"This is required to bypass the anti-bot protection and retrieve the stream link.",
headers: {
...commonHeaders,
Referer: link,
},
force: true,
waitForCookie: "cf_clearance",
});
console.log("WAF solved", wafResult.cookies);
cookies = wafResult.cookies;
dotlinkRes = await axios(`${link}`, {
headers: {
...commonHeaders,
Referer: link,
Cookie: cookies,
},
});
} else {
throw error;
}
}
const dotlinkText = dotlinkRes.data; const dotlinkText = dotlinkRes.data;
// console.log('dotlinkText', dotlinkText); // console.log('dotlinkText', dotlinkText);
const vlink = dotlinkText.match(/<a\s+href="([^"]*cloud\.[^"]*)"/i) || []; const vlink = dotlinkText.match(/<a\s+href="([^"]*cloud\.[^"]*)"/i) || [];
+36
View File
@@ -129,10 +129,46 @@ export interface ProviderType {
}) => Promise<Post[]>; }) => Promise<Post[]>;
} }
// Options to customize the WAF-solving WebView dialog.
// Options to customize the WAF-solving WebView dialog.
export interface OpenWebViewOptions {
// Title shown in the dialog header.
title?: string;
// Helper text shown under the title.
description?: string;
headers?: Record<string, string>;
waitForCookie?: string;
force?: boolean;
// If set, the dialog auto-cancels (rejects) after this many milliseconds.
timeoutMs?: number;
}
// Result returned to the provider after the user solves the challenge.
export interface OpenWebViewResult {
// The page response after the challenge is solved: the rendered HTML of the
// document (document.documentElement.outerHTML).
data: string;
// Cookie header value, e.g. "cf_clearance=abc; other=def".
cookies: string;
// Cookies as a name -> value map.
cookieMap: Record<string, string>;
// The User-Agent used by the WebView.
userAgent: string;
// The URL that was opened.
url: string;
}
export type ProviderContext = { export type ProviderContext = {
axios: AxiosStatic; axios: AxiosStatic;
Aes: any; // AES encryption utility, if used Aes: any; // AES encryption utility, if used
getBaseUrl: (providerValue: string) => Promise<string>; getBaseUrl: (providerValue: string) => Promise<string>;
commonHeaders: Record<string, string>; commonHeaders: Record<string, string>;
cheerio: typeof cheerio; cheerio: typeof cheerio;
openWebView: (
url: string,
options?: OpenWebViewOptions,
) => Promise<OpenWebViewResult>;
}; };