update
Fetch and Save Remote Content / fetch (push) Successful in 39s
Sync Versions to index.json / sync-versions (push) Failing after 54s

This commit is contained in:
aka paul
2026-05-27 20:30:23 +02:00
parent caffaabc88
commit 597f279e49
113 changed files with 1207 additions and 980 deletions
+337
View File
@@ -0,0 +1,337 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2(searchUrl);
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch
? (imageMatch[1].startsWith("http") ? imageMatch[1] : baseUrl + imageMatch[1])
: null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: imageSrc,
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2(url);
const htmlText = await response.text();
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2(actualUrl)).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2(episodeListUrl)).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
console.log(`[extractStreamUrl] ${type} ids:`, ids);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const dubType = url.includes("dub") ? "dub" : "sub";
const types = dubType === "sub" ? ["sub", "softsub"] : ["dub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewRes = await fetchv2(`https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
console.log(`[extractStreamUrl] fallback iframeUrl for ${type}:`, iframeUrl);
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { sub: "Hardsub English", softsub: "Original audio", dub: "Dubbed English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const proxyReqUrl = "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file);
const m3u8Response = await fetchv2(proxyReqUrl);
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
const nameMatch = line.match(/NAME="([^"]+)"/i) || line.match(/BANDWIDTH=(\d+)/i);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
} else if (nameMatch && nameMatch[1]) {
quality = nameMatch[1];
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+26
View File
@@ -0,0 +1,26 @@
{
"sourceName": "AnimeKai",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.4",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+312
View File
@@ -0,0 +1,312 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
console.log(htmlText);
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (tokenMatch && tokenMatch[1]) {
const rawToken = tokenMatch[1];
const encryptResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encryptData = await encryptResponse.json();
const encryptedToken = encryptData.result;
url = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
}
const fetchUrl = `${url}`;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(fetchUrl));
const text = await response.text();
const cleanedHtml = cleanJsonHtml(text);
const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/;
const softsubRegex = /<div class="server-items lang-group" data-id="softsub"[^>]*>([\s\S]*?)<\/div>/;
const dubRegex = /<div class="server-items lang-group" data-id="dub"[^>]*>([\s\S]*?)<\/div>/;
const subMatch = subRegex.exec(cleanedHtml);
const softsubMatch = softsubRegex.exec(cleanedHtml);
const dubMatch = dubRegex.exec(cleanedHtml);
const subContent = subMatch ? subMatch[1].trim() : "";
const softsubContent = softsubMatch ? softsubMatch[1].trim() : "";
const dubContent = dubMatch ? dubMatch[1].trim() : "";
const serverSpanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>Server 1<\/span>/;
const serverIdDub = serverSpanRegex.exec(dubContent)?.[1];
const serverIdSoftsub = serverSpanRegex.exec(softsubContent)?.[1];
const serverIdSub = serverSpanRegex.exec(subContent)?.[1];
const tokenRequestData = [
{ name: "Dub", data: serverIdDub },
{ name: "Softsub", data: serverIdSoftsub },
{ name: "Sub", data: serverIdSub }
].filter(item => item.data);
const tokenPromises = tokenRequestData.map(item =>
fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(item.data)}`)
.then(res => res.json())
.then(json => ({ name: item.name, data: json.result }))
.catch(err => ({ name: item.name, error: err.toString() }))
);
const tokenResults = await Promise.all(tokenPromises);
const streamUrls = tokenResults.map(result => {
const serverIdMap = {
"Dub": serverIdDub,
"Softsub": serverIdSoftsub,
"Sub": serverIdSub
};
return {
type: result.name,
url: `https://anikai.to/ajax/links/view?id=${serverIdMap[result.name]}&_=${result.data}`
};
});
const processStreams = async (streamUrls) => {
const streamResponses = await Promise.all(
streamUrls.map(async ({ type, url }) => {
try {
const res = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const json = await res.json();
return {
type: type,
result: json.result
};
} catch (error) {
console.log(`Error fetching ${type} stream:`, error);
return {
type: type,
result: null
};
}
})
);
const decryptRequestData = streamResponses
.filter(item => item.result)
.map(item => ({
name: item.type,
data: item.result
}));
if (decryptRequestData.length === 0) {
return {};
}
const decryptPromises = decryptRequestData.map(item =>
fetchv2(`https://enc-dec.app/api/dec-kai?text=${encodeURIComponent(item.data)}`)
.then(res => res.json())
.then(json => ({ name: item.name, data: JSON.stringify(json.result) }))
.catch(err => ({ name: item.name, error: err.toString() }))
);
const decryptResults = await Promise.all(decryptPromises);
const finalResults = {};
decryptResults.forEach(result => {
try {
const parsed = JSON.parse(result.data);
finalResults[result.name] = parsed.url;
console.log(`decrypted${result.name} URL:` + parsed.url);
} catch (error) {
console.log(`Error parsing ${result.name} result:`, error);
finalResults[result.name] = null;
}
});
return finalResults;
};
const decryptedUrls = await processStreams(streamUrls);
const decryptedSoftsub = decryptedUrls.Softsub || decryptedUrls.Dub || decryptedUrls.Sub;
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
};
if (decryptedSoftsub) {
const mediaUrl = decryptedSoftsub.replace("/e/", "/media/");
const response = await fetchv2(mediaUrl, headers);
const responseJson = await response.json();
const result = responseJson?.result;
const postData = {
"text": result,
"agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
}
const finalResponse = await fetchv2("https://enc-dec.app/api/dec-mega", { "Content-Type": "application/json" }, "POST", JSON.stringify(postData));
const finalJson = await finalResponse.json();
const m3u8Link = finalJson?.result?.sources?.[0]?.file;
const tracks = finalJson?.result?.tracks || [];
const arabicSub = tracks.find(track => track.label && (track.label.includes("Arabic") || track.label.includes("العربية")));
let subtitleUrl = null;
if (arabicSub) {
subtitleUrl = arabicSub.file;
} else {
const englishSub = tracks.find(track => track.label && track.label.includes("English"));
if (englishSub) {
subtitleUrl = englishSub.file;
}
}
const finalResult = {
stream: m3u8Link,
subtitles: subtitleUrl ? "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url="+ encodeURIComponent(subtitleUrl) : null
};
return JSON.stringify(finalResult);
}
return "error";
} catch (error) {
console.log("Fetch error:"+ error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeKai Arabic",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.0.1",
"language": "Arabic",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/arabic/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": true,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+324
View File
@@ -0,0 +1,324 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
console.log(htmlText);
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0",
"Accept": "text/html, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const types = ["dub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewUrl = `https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`;
const viewRes = await fetchv2(viewUrl);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { dub: "Dubbed English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeKai (Dub)",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.1.7",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/dub/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true
}
+317
View File
@@ -0,0 +1,317 @@
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const searchBaseUrl = "https://anikai.to/browser?keyword=";
const baseUrl = "https://anikai.to";
const posterHrefRegex = /href="[^"]*" class="poster"/g;
const titleRegex = /class="title"[^>]*title="[^"]*"/g;
const imageRegex = /data-src="[^"]*"/g;
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
try {
const encodedQuery = encodeQuery(query);
const searchUrl = searchBaseUrl + encodedQuery;
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(searchUrl));
const htmlText = await response.text();
const results = [];
const posterMatches = htmlText.match(posterHrefRegex) || [];
const titleMatches = htmlText.match(titleRegex) || [];
const imageMatches = htmlText.match(imageRegex) || [];
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
for (let index = 0; index < minLength; index++) {
const hrefMatch = posterMatches[index].match(extractHrefRegex);
const fullHref = hrefMatch ?
(hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) :
null;
const imageMatch = imageMatches[index].match(extractImageRegex);
const imageSrc = imageMatch ? imageMatch[1] : null;
const titleMatch = titleMatches[index].match(extractTitleRegex);
const cleanTitle = titleMatch ?
decodeHtmlEntities(titleMatch[1]) :
null;
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: fullHref,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
title: cleanTitle
});
}
}
return JSON.stringify(results);
} catch (error) {
return JSON.stringify([{
href: "",
image: "",
title: "Search failed: " + error.message
}]);
}
}
async function extractDetails(url) {
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url));
const htmlText = await response.text();
console.log(htmlText);
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
return JSON.stringify([{
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not available",
airdate: "If stream doesn't load try later or disable VPN/DNS"
}]);
} catch (error) {
console.error("Error fetching details:" + error);
return [{
description: "Error loading description",
aliases: "Aliases: Unknown",
airdate: "Aired: Unknown"
}];
}
}
async function extractEpisodes(url) {
try {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
const tokenResponse = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(animeIdMatch)}`);
const tokenData = await tokenResponse.json();
const token = tokenData.result;
const episodeListUrl = `https://anikai.to/ajax/episodes/list?ani_id=${animeIdMatch}&_=${token}`;
const episodeListData = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(episodeListUrl))).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
const episodes = episodeMatches.map(([_, episodeNum, episodeToken]) => ({
number: parseInt(episodeNum, 10),
href: `https://anikai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
}));
return JSON.stringify(episodes);
} catch (err) {
console.error("Error fetching episodes:" + err);
return [{
number: 1,
href: "Error fetching episodes"
}];
}
}
async function extractStreamUrl(url) {
const headers = {
"Referer": "https://anikai.to/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
};
try {
const tokenMatch = url.match(/token=([^&]+)/);
if (!tokenMatch?.[1]) throw new Error("No token found in URL");
const rawToken = tokenMatch[1];
const encTokenRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(rawToken)}`);
const encTokenData = await encTokenRes.json();
const encryptedToken = encTokenData.result;
const actualUrl = url.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
const response = await fetchv2(actualUrl);
const text = await response.text();
let ajaxResultHtml = "";
try {
const parsedAjax = JSON.parse(text);
ajaxResultHtml = parsedAjax?.result || "";
} catch {}
const cleanedHtml = cleanJsonHtml(text);
const cleanedAjaxResultHtml = cleanJsonHtml(ajaxResultHtml);
const serverHtmlSource = cleanedAjaxResultHtml || cleanedHtml;
const extractServerIds = (type) => {
const regex = new RegExp(`<div class="server-items lang-group" data-id="${type}"[^>]*>([\\s\\S]*?)<\\/div>`);
const content = regex.exec(serverHtmlSource)?.[1] ?? "";
const spanRegex = /<span class="server"[^>]*data-lid="([^"]+)"[^>]*>/g;
const ids = [];
let match;
while ((match = spanRegex.exec(content)) !== null) ids.push(match[1]);
return ids.length > 1 ? ids[1] : ids[0] ?? null;
};
const types = ["sub"];
const servers = types
.map(type => ({ type, lid: extractServerIds(type) }))
.filter(s => s.lid);
const streams = [];
const subtitles = [];
await Promise.all(servers.map(async ({ type, lid }) => {
try {
const decLidRes = await fetchv2(`https://enc-dec.app/api/enc-kai?text=${encodeURIComponent(lid)}`);
const decLidData = await decLidRes.json();
const decodedLid = decLidData.result;
const viewUrl = `https://anikai.to/ajax/links/view?id=${lid}&_=${decodedLid}`;
const viewRes = await fetchv2(viewUrl);
const viewJson = await viewRes.json();
const encodedResult = viewJson.result;
const decRes = await fetchv2(
"https://enc-dec.app/api/dec-kai",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedResult })
);
const decJson = await decRes.json();
let iframeUrl = decJson.result?.url ?? null;
if (!iframeUrl) return;
if (iframeUrl.includes("anikai.to/iframe")) {
const iframePageRes = await fetchv2(iframeUrl, headers);
const iframePageText = await iframePageRes.text();
const iframeSrcMatch = iframePageText.match(/<iframe[^>]+src="([^"]+)"/i);
if (iframeSrcMatch && iframeSrcMatch[1]) {
iframeUrl = iframeSrcMatch[1];
}
}
const mediaUrl = iframeUrl.replace("/e/", "/media/").replace("/e2/", "/media/");
const mediaRes = await fetchv2(mediaUrl, headers);
const mediaJson = await mediaRes.json();
const encodedM3u8 = mediaJson?.result;
if (!encodedM3u8) return;
const finalRes = await fetchv2(
"https://enc-dec.app/api/dec-mega",
{ "Content-Type": "application/json" },
"POST",
JSON.stringify({ text: encodedM3u8, agent: headers["User-Agent"] })
);
const finalJson = await finalRes.json();
const sources = finalJson?.result?.sources ?? [];
const tracks = finalJson?.result?.tracks ?? [];
const file = sources[0]?.file ?? null;
if (file) {
const titleMap = { sub: "Hardsub English" };
let pushedQualities = 0;
const baseTitle = titleMap[type] || type;
try {
const m3u8Response = await fetchv2("https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file));
const m3u8Text = await m3u8Response.text();
const lines = m3u8Text.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resolutionMatch = line.match(/RESOLUTION=(\d+x\d+)/);
let quality = 'Unknown';
if (resolutionMatch) {
const [width, height] = resolutionMatch[1].split('x');
quality = `${height}p`;
}
if (i + 1 < lines.length) {
let streamPath = lines[i + 1].trim();
let absolutePath;
if (streamPath.startsWith('/api/')) {
absolutePath = 'https://1anime.app' + streamPath;
} else if (streamPath.startsWith('http')) {
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(streamPath);
} else {
const baseUrl = file.substring(0, file.lastIndexOf('/') + 1);
absolutePath = 'https://1anime.app/api/m3u8-proxy?url=' + encodeURIComponent(baseUrl + streamPath);
}
streams.push({
title: `${quality} - ${baseTitle}`,
streamUrl: absolutePath
});
pushedQualities++;
}
}
}
} catch (e) {
console.log("Failed to extract qualities:", e);
}
if (pushedQualities === 0) {
streams.push({ title: baseTitle, streamUrl: "https://1anime.app/api/m3u8-proxy?url=" + encodeURIComponent(file) });
}
}
for (const track of tracks) {
if (track.file && track.label) {
subtitles.push({ url: track.file, language: track.label });
}
}
} catch (e) {
console.log(`[extractStreamUrl] error for ${type}:`, e.toString());
}
}));
return JSON.stringify({ streams, subtitles });
} catch (error) {
console.error("Animekai fetch error:" + error);
return "https://error.org";
}
}
function cleanHtmlSymbols(string) {
if (!string) {
return "";
}
return string
.replace(/&#8217;/g, "'")
.replace(/&#8211;/g, "-")
.replace(/&#[0-9]+;/g, "")
.replace(/\r?\n|\r/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function cleanJsonHtml(jsonHtml) {
if (!jsonHtml) {
return "";
}
return jsonHtml
.replace(/\\"/g, "\"")
.replace(/\\'/g, "'")
.replace(/\\\\/g, "\\")
.replace(/\\n/g, "\n")
.replace(/\\t/g, "\t")
.replace(/\\r/g, "\r");
}
function decodeHtmlEntities(text) {
if (!text) {
return "";
}
return text
.replace(/&#039;/g, "'")
.replace(/&quot;/g, "\"")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&nbsp;/g, " ");
}
+27
View File
@@ -0,0 +1,27 @@
{
"sourceName": "AnimeKai (Hardsub)",
"iconUrl": "https://apktodo.io/uploads/2025/5/animekai-icon.jpg",
"author": {
"name": "50/50",
"icon": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ3122kQwublLkZ6rf1fEpUP79BxZOFmH9BSA&s"
},
"version": "1.1.6",
"language": "English",
"streamType": "HLS",
"quality": "1080p",
"baseUrl": "https://animekai.to/",
"searchBaseUrl": "https://animekai.to/",
"scriptUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/animekai/hardsub/animekai.js",
"type": "anime",
"asyncJS": true,
"softsub": false,
"downloadSupport": true,
"note": "Make sure you're on the latest version of Sora.",
"supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true,
"supportsLuna": true,
"supportsAnymex": true,
"supportsTsumi": true,
"supportsHiyoku": true
}