updates and new extensions
This commit is contained in:
189
anime/AnimePahe.js
Normal file
189
anime/AnimePahe.js
Normal file
@@ -0,0 +1,189 @@
|
||||
class AnimePahe {
|
||||
constructor() {
|
||||
this.baseUrl = "https://animepahe.si";
|
||||
this.api = "https://animepahe.si";
|
||||
this.type = "anime-board";
|
||||
this.version = "1.0";
|
||||
this.headers = { Referer: "https://kwik.cx" };
|
||||
}
|
||||
|
||||
getSettings() {
|
||||
return {
|
||||
episodeServers: ["Kwik", "Pahe"],
|
||||
supportsDub: false,
|
||||
};
|
||||
}
|
||||
|
||||
async search(queryObj) {
|
||||
const req = await fetch(
|
||||
`${this.api}/api?m=search&q=${encodeURIComponent(queryObj.query)}`,
|
||||
{ headers: { Cookie: "__ddg1_=;__ddg2_=;" } }
|
||||
);
|
||||
|
||||
if (!req.ok) return [];
|
||||
const data = await req.json();
|
||||
if (!data?.data) return [];
|
||||
|
||||
return data.data.map((item) => ({
|
||||
id: item.session,
|
||||
title: item.title,
|
||||
url: "",
|
||||
subOrDub: "sub",
|
||||
}));
|
||||
}
|
||||
|
||||
async findEpisodes(id) {
|
||||
let episodes = [];
|
||||
|
||||
const req = await fetch(
|
||||
`${this.api}${id.includes("-") ? `/anime/${id}` : `/a/${id}`}`,
|
||||
{ headers: { Cookie: "__ddg1_=;__ddg2_=;" } }
|
||||
);
|
||||
|
||||
const html = await req.text();
|
||||
const $ = this.cheerio.load(html);
|
||||
|
||||
const tempId = $("head > meta[property='og:url']")
|
||||
.attr("content")
|
||||
.split("/")
|
||||
.pop();
|
||||
|
||||
const pushData = (data) => {
|
||||
for (const item of data) {
|
||||
episodes.push({
|
||||
id: item.session + "$" + id,
|
||||
number: item.episode,
|
||||
title:
|
||||
item.title && item.title.length > 0
|
||||
? item.title
|
||||
: "Episode " + item.episode,
|
||||
url: req.url,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const first = await fetch(
|
||||
`${this.api}/api?m=release&id=${tempId}&sort=episode_asc&page=1`,
|
||||
{ headers: { Cookie: "__ddg1_=;__ddg2_=;" } }
|
||||
).then((r) => r.json());
|
||||
|
||||
pushData(first.data);
|
||||
|
||||
const pages = Array.from(
|
||||
{ length: first.last_page - 1 },
|
||||
(_, i) => i + 2
|
||||
);
|
||||
|
||||
const results = await Promise.all(
|
||||
pages.map((p) =>
|
||||
fetch(
|
||||
`${this.api}/api?m=release&id=${tempId}&sort=episode_asc&page=${p}`,
|
||||
{ headers: { Cookie: "__ddg1_=;__ddg2_=;" } }
|
||||
).then((r) => r.json())
|
||||
)
|
||||
);
|
||||
|
||||
results.forEach((r) => r.data && pushData(r.data));
|
||||
|
||||
episodes.sort((a, b) => a.number - b.number);
|
||||
|
||||
if (!episodes.length) throw new Error("No episodes found.");
|
||||
|
||||
const lowest = episodes[0].number;
|
||||
if (lowest > 1) {
|
||||
episodes.forEach((ep) => (ep.number = ep.number - lowest + 1));
|
||||
}
|
||||
|
||||
return episodes.filter((ep) => Number.isInteger(ep.number));
|
||||
}
|
||||
|
||||
async findEpisodeServer(episodeOrId, server) {
|
||||
const [episodeId, animeId] = episodeOrId.id.split("$");
|
||||
|
||||
const req = await fetch(
|
||||
`${this.api}/play/${animeId}/${episodeId}`,
|
||||
{ headers: { Cookie: "__ddg1_=;__ddg2_=;" } }
|
||||
);
|
||||
|
||||
const html = await req.text();
|
||||
const matches = html.match(/https:\/\/kwik\.cx\/e\/\w+/g);
|
||||
if (!matches) throw new Error("Failed to fetch episode server.");
|
||||
|
||||
const $ = this.cheerio.load(html);
|
||||
|
||||
const sourcePromises = $("button[data-src]")
|
||||
.toArray()
|
||||
.map(async (el) => {
|
||||
const embedUrl = $(el).data("src");
|
||||
if (!embedUrl) return null;
|
||||
|
||||
const fansub = $(el).data("fansub");
|
||||
const quality = $(el).data("resolution");
|
||||
|
||||
let label = `${quality}p - ${fansub}`;
|
||||
if ($(el).data("audio") === "eng") label += " (Eng)";
|
||||
if (embedUrl === matches[0]) label += " (default)";
|
||||
|
||||
try {
|
||||
const srcReq = await fetch(embedUrl, {
|
||||
headers: {
|
||||
Referer: this.headers.Referer,
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
|
||||
},
|
||||
});
|
||||
|
||||
const srcHtml = await srcReq.text();
|
||||
const scripts = srcHtml.match(/eval\(f.+?\}\)\)/g);
|
||||
if (!scripts) return null;
|
||||
|
||||
for (const s of scripts) {
|
||||
const m = s.match(/eval(.+)/);
|
||||
if (!m?.[1]) continue;
|
||||
|
||||
try {
|
||||
const decoded = eval(m[1]);
|
||||
const link = decoded.match(/source='(.+?)'/);
|
||||
if (!link?.[1]) continue;
|
||||
|
||||
const m3u8 = link[1];
|
||||
|
||||
if (server === "Pahe") {
|
||||
return {
|
||||
url: m3u8
|
||||
.replace("owocdn.top", "kwik.cx")
|
||||
.replace("/stream/", "/mp4/")
|
||||
.replace("/uwu.m3u8", ""),
|
||||
type: "mp4",
|
||||
quality: label,
|
||||
subtitles: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
url: m3u8,
|
||||
type: "m3u8",
|
||||
quality: label,
|
||||
subtitles: [],
|
||||
};
|
||||
} catch {}
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const videoSources = (await Promise.all(sourcePromises)).filter(Boolean);
|
||||
if (!videoSources.length)
|
||||
throw new Error(`Failed to extract any sources for ${server}.`);
|
||||
|
||||
return {
|
||||
server,
|
||||
headers: this.headers,
|
||||
videoSources,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AnimePahe;
|
||||
Reference in New Issue
Block a user