From 725c0eaed200cc3a9373a123cce9f48462ee3f9e Mon Sep 17 00:00:00 2001 From: himanshu8443 Date: Sun, 27 Jul 2025 13:09:27 +0530 Subject: [PATCH] feat: update catalog titles and enhance stream functionality with quality and subtitle support --- dist/animetsu/catalog.js | 2 +- dist/animetsu/posts.js | 2 +- dist/animetsu/stream.js | 2 +- providers/animetsu/catalog.ts | 2 +- providers/animetsu/posts.ts | 4 +- providers/animetsu/stream.ts | 87 ++++++++++++++++++++++++++++++----- 6 files changed, 81 insertions(+), 18 deletions(-) diff --git a/dist/animetsu/catalog.js b/dist/animetsu/catalog.js index 16d7e3f..f033b81 100644 --- a/dist/animetsu/catalog.js +++ b/dist/animetsu/catalog.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.genres=exports.catalog=void 0,exports.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:"Recently Updated",filter:"/api/anime/search?query=&page=1&perPage=35&year=any&sort=updated&season=any&format=any&status=any"}],exports.genres=[]; \ No newline at end of file +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.genres=exports.catalog=void 0,exports.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"}],exports.genres=[]; \ No newline at end of file diff --git a/dist/animetsu/posts.js b/dist/animetsu/posts.js index acc4397..332fdd6 100644 --- a/dist/animetsu/posts.js +++ b/dist/animetsu/posts.js @@ -1 +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.getSearchPosts=exports.getPosts=void 0;const getPosts=function(_a){return __awaiter(this,arguments,void 0,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){const{axios:axios}=providerContext,url=new URL("https://backend.animetsu.to"+filter);return url.searchParams.set("page",page.toString()),posts({url:url.toString(),signal:signal,axios:axios})})};exports.getPosts=getPosts;const getSearchPosts=function(_a){return __awaiter(this,arguments,void 0,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})})};function posts(_a){return __awaiter(this,arguments,void 0,function*({url:url,signal:signal,axios:axios}){var _b;try{const data=null===(_b=(yield axios.get(url,{signal:signal,headers:{Referer:"https://animetsu.to/"}})).data)||void 0===_b?void 0:_b.results,catalog=[];return null==data||data.map(element=>{var _a,_b,_c,_d,_e,_f,_g;const title=(null===(_a=element.title)||void 0===_a?void 0:_a.english)||(null===(_b=element.title)||void 0===_b?void 0:_b.romaji)||(null===(_c=element.title)||void 0===_c?void 0:_c.native),link=null===(_d=element.id)||void 0===_d?void 0:_d.toString(),image=(null===(_e=element.coverImage)||void 0===_e?void 0:_e.large)||(null===(_f=element.coverImage)||void 0===_f?void 0:_f.extraLarge)||(null===(_g=element.coverImage)||void 0===_g?void 0:_g.medium);title&&link&&image&&catalog.push({title:title,link:link,image:image})}),catalog}catch(err){return[]}})}exports.getSearchPosts=getSearchPosts; \ No newline at end of file +"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.getSearchPosts=exports.getPosts=void 0;const getPosts=function(_a){return __awaiter(this,arguments,void 0,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){const{axios:axios}=providerContext;return posts({url:("https://backend.animetsu.to"+filter+"&page="+page.toString()).toString(),signal:signal,axios:axios})})};exports.getPosts=getPosts;const getSearchPosts=function(_a){return __awaiter(this,arguments,void 0,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})})};function posts(_a){return __awaiter(this,arguments,void 0,function*({url:url,signal:signal,axios:axios}){var _b;try{const data=null===(_b=(yield axios.get(url,{signal:signal,headers:{Referer:"https://animetsu.to/"}})).data)||void 0===_b?void 0:_b.results,catalog=[];return null==data||data.map(element=>{var _a,_b,_c,_d,_e,_f,_g;const title=(null===(_a=element.title)||void 0===_a?void 0:_a.english)||(null===(_b=element.title)||void 0===_b?void 0:_b.romaji)||(null===(_c=element.title)||void 0===_c?void 0:_c.native),link=null===(_d=element.id)||void 0===_d?void 0:_d.toString(),image=(null===(_e=element.coverImage)||void 0===_e?void 0:_e.large)||(null===(_f=element.coverImage)||void 0===_f?void 0:_f.extraLarge)||(null===(_g=element.coverImage)||void 0===_g?void 0:_g.medium);title&&link&&image&&catalog.push({title:title,link:link,image:image})}),catalog}catch(err){return[]}})}exports.getSearchPosts=getSearchPosts; \ No newline at end of file diff --git a/dist/animetsu/stream.js b/dist/animetsu/stream.js index f616573..89e90eb 100644 --- a/dist/animetsu/stream.js +++ b/dist/animetsu/stream.js @@ -1 +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=void 0;const getStream=function(_a){return __awaiter(this,arguments,void 0,function*({link:id,providerContext:providerContext}){try{const{axios:axios}=providerContext,baseUrl="https://backend.animetsu.to",[animeId,episodeNumber]=id.split(":");if(!animeId||!episodeNumber)throw new Error("Invalid link format");const servers=["pahe","zoro"],streamLinks=[];return yield Promise.all(servers.map(server=>__awaiter(this,void 0,void 0,function*(){try{const url=`${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=sub`,res=yield axios.get(url,{headers:{Referer:"https://animetsu.to/"}});res.data&&res.data.sources&&res.data.sources.forEach(source=>{streamLinks.push({server:server,link:source.url,type:source.url.includes(".m3u8")?"m3u8":"mp4",quality:source.quality,headers:{Referer:"https://animetsu.to/",Origin:"https://animetsu.to"},subtitles:[]})})}catch(e){}}))),yield Promise.all(servers.map(server=>__awaiter(this,void 0,void 0,function*(){try{const url=`${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=dub`,res=yield axios.get(url,{headers:{Referer:"https://animetsu.to/"}});res.data&&res.data.sources&&res.data.sources.forEach(source=>{streamLinks.push({server:`${server} (Dub)`,link:source.url,type:source.url.includes(".m3u8")?"m3u8":"mp4",quality:source.quality,headers:{Referer:"https://animetsu.to/",Origin:"https://animetsu.to"},subtitles:[]})})}catch(e){}}))),streamLinks}catch(err){return[]}})};exports.getStream=getStream; \ No newline at end of file +"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=void 0;const getStream=function(_a){return __awaiter(this,arguments,void 0,function*({link:id,providerContext:providerContext}){try{const{axios:axios}=providerContext,baseUrl="https://backend.animetsu.to",[animeId,episodeNumber]=id.split(":");if(!animeId||!episodeNumber)throw new Error("Invalid link format");const servers=["pahe","zoro"],streamLinks=[];return yield Promise.all(servers.map(server=>__awaiter(this,void 0,void 0,function*(){try{const url=`${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=sub`,res=yield axios.get(url,{headers:{Referer:"https://animetsu.to/"}});if(res.data&&res.data.sources){const subtitles=[];res.data.sources.forEach(source=>{streamLinks.push({server:server+`: ${source.quality}`,link:source.url,type:"m3u8",quality:source.quality,headers:{referer:"https://animetsu.to/"},subtitles:subtitles.length>0?subtitles:[]})})}}catch(e){}}))),yield Promise.all(servers.map(server=>__awaiter(this,void 0,void 0,function*(){try{const url=`${baseUrl}/api/anime/tiddies?server=${server}&id=${animeId}&num=${episodeNumber}&subType=dub`,res=yield axios.get(url,{headers:{referer:"https://animetsu.to/"}});if(res.data&&res.data.sources){const subtitles=[];res.data.sources.forEach(source=>{streamLinks.push({server:`${server} (Dub) : ${source.quality}`,link:source.url,type:"m3u8",quality:source.quality,headers:{referer:"https://animetsu.to/"},subtitles:subtitles.length>0?subtitles:[]})})}}catch(e){}}))),streamLinks}catch(err){return[]}})};exports.getStream=getStream; \ No newline at end of file diff --git a/providers/animetsu/catalog.ts b/providers/animetsu/catalog.ts index 498c8df..ac14620 100644 --- a/providers/animetsu/catalog.ts +++ b/providers/animetsu/catalog.ts @@ -15,7 +15,7 @@ export const catalog = [ "/api/anime/search?query=&page=1&perPage=35&year=any&sort=rating&season=any&format=any&status=any", }, { - title: "Recently Updated", + title: "Favourites", filter: "/api/anime/search?query=&page=1&perPage=35&year=any&sort=updated&season=any&format=any&status=any", }, diff --git a/providers/animetsu/posts.ts b/providers/animetsu/posts.ts index 8911ce6..097f4ee 100644 --- a/providers/animetsu/posts.ts +++ b/providers/animetsu/posts.ts @@ -16,8 +16,8 @@ export const getPosts = async function ({ const baseUrl = "https://backend.animetsu.to"; // Parse filter to modify page parameter - const url = new URL(baseUrl + filter); - url.searchParams.set("page", page.toString()); + const url = baseUrl + filter + "&page=" + page.toString(); + console.log("animetsuGetPosts url", url); return posts({ url: url.toString(), signal, axios }); }; diff --git a/providers/animetsu/stream.ts b/providers/animetsu/stream.ts index d433cb0..0106ac2 100644 --- a/providers/animetsu/stream.ts +++ b/providers/animetsu/stream.ts @@ -1,4 +1,4 @@ -import { Stream, ProviderContext } from "../types"; +import { Stream, ProviderContext, TextTracks } from "../types"; export const getStream = async function ({ link: id, @@ -33,17 +33,48 @@ export const getStream = async function ({ }); if (res.data && res.data.sources) { + const subtitles: TextTracks = []; + // if (res.data.subtitles && Array.isArray(res.data.subtitles)) { + // res.data.subtitles.forEach((sub: any) => { + // 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") + // ? "en" + // : sub.lang.toLowerCase().includes("arabic") + // ? "ar" + // : sub.lang.toLowerCase().includes("french") + // ? "fr" + // : sub.lang.toLowerCase().includes("german") + // ? "de" + // : sub.lang.toLowerCase().includes("italian") + // ? "it" + // : sub.lang.toLowerCase().includes("portuguese") + // ? "pt" + // : sub.lang.toLowerCase().includes("russian") + // ? "ru" + // : sub.lang.toLowerCase().includes("spanish") + // ? "es" + // : "und"; + + // subtitles.push({ + // title: sub.lang, + // language: langCode, + // type: "text/vtt", + // uri: sub.url, + // }); + // } + // }); + // } res.data.sources.forEach((source: any) => { streamLinks.push({ - server: server, + server: server + `: ${source.quality}`, link: source.url, - type: source.url.includes(".m3u8") ? "m3u8" : "mp4", + type: "m3u8", quality: source.quality, headers: { - Referer: "https://animetsu.to/", - Origin: "https://animetsu.to", + referer: "https://animetsu.to/", }, - subtitles: [], // No subtitle info provided in API response + subtitles: subtitles.length > 0 ? subtitles : [], }); }); } @@ -61,22 +92,53 @@ export const getStream = async function ({ const res = await axios.get(url, { headers: { - Referer: "https://animetsu.to/", + referer: "https://animetsu.to/", }, }); if (res.data && res.data.sources) { + const subtitles: TextTracks = []; + // if (res.data.subtitles && Array.isArray(res.data.subtitles)) { + // res.data.subtitles.forEach((sub: any) => { + // 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") + // ? "en" + // : sub.lang.toLowerCase().includes("arabic") + // ? "ar" + // : sub.lang.toLowerCase().includes("french") + // ? "fr" + // : sub.lang.toLowerCase().includes("german") + // ? "de" + // : sub.lang.toLowerCase().includes("italian") + // ? "it" + // : sub.lang.toLowerCase().includes("portuguese") + // ? "pt" + // : sub.lang.toLowerCase().includes("russian") + // ? "ru" + // : sub.lang.toLowerCase().includes("spanish") + // ? "es" + // : "und"; + + // subtitles.push({ + // title: sub.lang, + // language: langCode, + // type: "text/vtt", + // uri: sub.url, + // }); + // } + // }); + // } res.data.sources.forEach((source: any) => { streamLinks.push({ - server: `${server} (Dub)`, + server: `${server} (Dub) : ${source.quality}`, link: source.url, - type: source.url.includes(".m3u8") ? "m3u8" : "mp4", + type: "m3u8", quality: source.quality, headers: { - Referer: "https://animetsu.to/", - Origin: "https://animetsu.to", + referer: "https://animetsu.to/", }, - subtitles: [], + subtitles: subtitles.length > 0 ? subtitles : [], }); }); } @@ -86,6 +148,7 @@ export const getStream = async function ({ }) ); + console.log("Stream links:", streamLinks); return streamLinks; } catch (err) { console.error("animetsu stream error:", err);