///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Main Functions //////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
async function searchResults(keyword) {
try {
const encodedKeyword = encodeURIComponent(keyword);
const searchApiUrl = `https://aniworld.to/ajax/seriesSearch?keyword=${encodedKeyword}`;
const responseText = await fetch(searchApiUrl);
const data = await JSON.parse(responseText);
const transformedResults = data.map((anime) => ({
title: anime.name,
image: `https://aniworld.to${anime.cover}`,
href: `https://aniworld.to/anime/stream/${anime.link}`,
}));
return JSON.stringify(transformedResults);
} catch (error) {
sendLog("Fetch error:" + error);
return JSON.stringify([{ title: "Error", image: "", href: "" }]);
}
}
async function extractDetails(url) {
try {
const fetchUrl = `${url}`;
const text = await fetch(fetchUrl);
const descriptionRegex =
/
(.*?)<\/p>/s;
const aliasesRegex = /
]*\bdata-alternativetitles="([^"]+)"[^>]*>/i;
const aliasesMatch = aliasesRegex.exec(text);
let aliasesArray = [];
if (aliasesMatch) {
aliasesArray = aliasesMatch[1].split(",").map((a) => a.trim());
}
const descriptionMatch = descriptionRegex.exec(text) || [];
const airdateMatch = "Unknown"; // TODO: Implement airdate extraction
const transformedResults = [
{
description: descriptionMatch[1] || "No description available",
aliases: aliasesArray[0] || "No aliases available",
airdate: airdateMatch,
},
];
return JSON.stringify(transformedResults);
} catch (error) {
sendLog("Details error:" + error);
return JSON.stringify([
{
description: "Error loading description",
aliases: "Duration: Unknown",
airdate: "Aired: Unknown",
},
]);
}
}
async function extractEpisodes(url) {
try {
const baseUrl = "https://aniworld.to";
const fetchUrl = `${url}`;
const html = await fetch(fetchUrl);
const finishedList = [];
const seasonLinks = getSeasonLinks(html);
for (const seasonLink of seasonLinks) {
const seasonEpisodes = await fetchSeasonEpisodes(
`${baseUrl}${seasonLink}`
);
finishedList.push(...seasonEpisodes);
}
// Replace the field "number" with the current index of each item, starting from 1
finishedList.forEach((item, index) => {
item.number = index + 1;
});
return JSON.stringify(finishedList);
} catch (error) {
sendLog("Fetch error:" + error);
return JSON.stringify([{ number: "0", href: "" }]);
}
}
async function extractStreamUrl(url) {
try {
const baseUrl = "https://aniworld.to";
const fetchUrl = `${url}`;
const text = await fetch(fetchUrl);
const finishedList = [];
const languageList = getAvailableLanguages(text);
const videoLinks = getVideoLinks(text);
for (const videoLink of videoLinks) {
const language = languageList.find(
(l) => l.langKey === videoLink.langKey
);
if (language) {
finishedList.push({
provider: videoLink.provider,
href: `${baseUrl}${videoLink.href}`,
language: language.title,
});
}
}
// Select the hoster
const selectedHoster = selectHoster(finishedList);
const provider = selectedHoster.provider;
const providerLink = selectedHoster.href;
if (provider === "Error") {
sendLog("No video found");
return JSON.stringify([{ provider: "Error", link: "" }]);
}
sendLog("Selected provider: " + provider);
sendLog("Selected link: " + providerLink);
const videoPage = await fetch(providerLink);
sendLog("Video Page: " + videoPage.length);
const winLocRegex = /window\.location\.href\s*=\s*['"]([^'"]+)['"]/;
const winLocMatch = winLocRegex.exec(videoPage);
let winLocUrl = null;
if (!winLocMatch) {
winLocUrl = providerLink;
} else {
winLocUrl = winLocMatch[1];
}
const hlsSourceResponse = await fetch(winLocUrl);
const hlsSourcePage =
typeof hlsSourceResponse === "object"
? await hlsSourceResponse.text()
: await hlsSourceResponse;
sendLog("Provider: " + provider);
sendLog("URL: " + winLocUrl);
sendLog("HLS Source Page: " + hlsSourcePage.length);
switch (provider) {
case "VOE":
try {
const voeJson = voeExtractor(hlsSourcePage);
return voeJson?.source || JSON.stringify([{ provider: "Error", link: "" }]);
} catch (error) {
sendLog("VOE extractor error: " + error);
return JSON.stringify([{ provider: "Error", link: "" }]);
}
case "SpeedFiles":
try {
const speedfilesUrl = await speedfilesExtractor(hlsSourcePage);
return speedfilesUrl || JSON.stringify([{ provider: "Error", link: "" }]);
} catch (error) {
sendLog("Speedfiles extractor error: " + error);
return JSON.stringify([{ provider: "Error", link: "" }]);
}
case "Vidmoly":
try {
const vidmolyUrl = vidmolyExtractor(hlsSourcePage);
return vidmolyUrl || JSON.stringify([{ provider: "Error", link: "" }]);
} catch (error) {
sendLog("Vidmoly extractor error: " + error);
return JSON.stringify([{ provider: "Error", link: "" }]);
}
default:
sendLog("Unsupported provider:", provider);
return JSON.stringify([{ provider: "Error", link: "" }]);
}
// END OF VOE EXTRACTOR
// Extract the sources variable and decode the hls value from base64
const sourcesRegex = /var\s+sources\s*=\s*({[^}]+})/;
const sourcesMatch = sourcesRegex.exec(hlsSourcePage);
let sourcesString = sourcesMatch
? sourcesMatch[1].replace(/'/g, '"')
: null;
return sourcesString;
} catch (error) {
sendLog("ExtractStreamUrl error:" + error);
return JSON.stringify([{ provider: "Error1", link: "" }]);
}
}
function selectHoster(finishedList) {
let firstVideo = null;
let provider = null;
// Define the preferred providers and languages
const providerList = ["Vidmoly", "SpeedFiles", "VOE"];
const languageList = ["Deutsch", "mit Untertitel Deutsch"];
for (const providerName of providerList) {
for (const language of languageList) {
const video = finishedList.find(
(video) => video.provider === providerName && video.language === language
);
if (video) {
provider = providerName;
firstVideo = video;
break;
}
}
if (firstVideo) break;
}
// Default to the first video if no match is found
if (!firstVideo) {
firstVideo = finishedList[0];
}
if (firstVideo) {
return {
provider: provider,
href: firstVideo.href,
};
} else {
sendLog("No video found");
return {
provider: "Error",
href: "https://error.org",
};
}
}
//Thanks to Ibro and Cufiy
async function vidmolyExtractor(html) {
sendLog("Vidmoly extractor");
sendLog(html);
const regexSub = /