Update ashi/ashi.js

This commit is contained in:
aka paul
2026-03-14 21:04:25 +00:00
parent b856529fce
commit 71e8747998
+190 -40
View File
@@ -4,8 +4,52 @@
//
//
const DENO_PROXY_PREFIX = "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=";
const ANIKAI_HOME_TITLE_REGEX = /<title>Home - AnimeKai - Watch Free Anime Online, Stream Subbed &amp; Dubbed Anime in HD<\/title>/i;
const ANIKAI_CHECK_TIMEOUT_MS = 900;
let animekaiBlockCheckPromise = null;
function proxyUrl(url) {
return DENO_PROXY_PREFIX + encodeURIComponent(url);
}
async function isAnimekaiBlockedForUser() {
if (!animekaiBlockCheckPromise) {
animekaiBlockCheckPromise = (async () => {
let timeoutId;
try {
const htmlText = await Promise.race([
fetchv2("https://anikai.to/home").then(response => response.text()),
new Promise(resolve => {
timeoutId = setTimeout(() => resolve(""), ANIKAI_CHECK_TIMEOUT_MS);
})
]);
if (timeoutId) {
clearTimeout(timeoutId);
}
return !ANIKAI_HOME_TITLE_REGEX.test(htmlText);
} catch (error) {
if (timeoutId) {
clearTimeout(timeoutId);
}
console.error("Animekai accessibility check failed:" + error);
return true;
}
})();
}
return animekaiBlockCheckPromise;
}
async function searchResults(query) {
const encodeQuery = keyword => encodeURIComponent(keyword);
const MIN_RESULTS_FAST_RETURN = 8;
const HIGH_CONFIDENCE_SCORE = 900;
const queryNormalized = query.toLowerCase().trim();
const queryTokens = queryNormalized.split(/\s+/).filter(Boolean);
const isSpecificQuery = queryTokens.length <= 2 && queryNormalized.length >= 5;
const decodeHtmlEntities = (str) => {
if (!str) return str;
@@ -59,6 +103,14 @@ async function searchResults(query) {
if (!isStopword) significantMatches++;
}
else if (qToken.length >= 4 && tToken.length >= 4) {
if (Math.abs(qToken.length - tToken.length) > 2) {
return;
}
if (qToken[0] !== tToken[0]) {
return;
}
const dist = levenshteinDistance(qToken, tToken);
const maxLen = Math.max(qToken.length, tToken.length);
const similarity = 1 - (dist / maxLen);
@@ -149,6 +201,7 @@ async function searchResults(query) {
const extractHrefRegex = /href="([^"]*)"/;
const extractImageRegex = /data-src="([^"]*)"/;
const extractTitleRegex = /title="([^"]*)"/;
let useProxy = true;
const extractResultsFromHTML = (htmlText) => {
const results = [];
@@ -170,7 +223,7 @@ async function searchResults(query) {
if (fullHref && imageSrc && cleanTitle) {
results.push({
href: `Animekai:${fullHref}`,
image: "https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(imageSrc),
image: useProxy ? proxyUrl(imageSrc) : imageSrc,
title: cleanTitle
});
}
@@ -181,21 +234,33 @@ async function searchResults(query) {
try {
const encodedQuery = encodeQuery(query);
const urls = [
`${searchBaseUrl}${encodedQuery}`,
`${searchBaseUrl}${encodedQuery}&page=2`,
`${searchBaseUrl}${encodedQuery}&page=3`
];
useProxy = await isAnimekaiBlockedForUser();
const fetchPages = async (pages) => {
const urls = pages.map(page =>
page === 1
? `${searchBaseUrl}${encodedQuery}`
: `${searchBaseUrl}${encodedQuery}&page=${page}`
);
const responses = await Promise.all(urls.map(url => fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const responses = await Promise.all(urls.map(url => fetchv2(useProxy ? proxyUrl(url) : url)));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
};
const page1Results = await fetchPages([1]);
return {
page1Results,
fetchRemaining: () => fetchPages([2, 3])
};
} catch (error) {
console.error("Animekai search error:" + error);
return [];
return {
page1Results: [],
fetchRemaining: async () => []
};
}
};
@@ -228,41 +293,123 @@ async function searchResults(query) {
try {
const encodedQuery = encodeQuery(query);
const urls = [
`${searchBaseUrl}${encodedQuery}`,
`${searchBaseUrl}${encodedQuery}&page=2`,
`${searchBaseUrl}${encodedQuery}&page=3`
];
const fetchPages = async (pages) => {
const urls = pages.map(page =>
page === 1
? `${searchBaseUrl}${encodedQuery}`
: `${searchBaseUrl}${encodedQuery}&page=${page}`
);
const responses = await Promise.all(urls.map(url => fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const responses = await Promise.all(urls.map(url => fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(url))));
const htmlTexts = await Promise.all(responses.map(res => res.text()));
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
const allResults = [];
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
return allResults;
};
const page1Results = await fetchPages([1]);
return {
page1Results,
fetchRemaining: () => fetchPages([2, 3])
};
} catch (error) {
console.error("1Movies search error:" + error);
return [];
return {
page1Results: [],
fetchRemaining: async () => []
};
}
};
try {
const [animekaiResults, oneMoviesResults] = await Promise.all([
animekaiSearch(),
oneMoviesSearch()
const animekaiPromise = animekaiSearch();
const animekaiData = await animekaiPromise;
const dedupeResults = (results) => {
const seen = new Set();
return results.filter(item => {
const key = `${item.href}|${item.title}`;
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
};
const scoreCache = new Map();
const rankResults = (results) => {
const scoredResults = results.map(r => {
const cacheKey = r.title;
const cached = scoreCache.get(cacheKey);
const score = cached !== undefined ? cached : fuzzyMatch(query, r.title);
if (cached === undefined) {
scoreCache.set(cacheKey, score);
}
return {
...r,
score
};
});
const sorted = scoredResults
.filter(r => r.score > 50)
.sort((a, b) => b.score - a.score);
return {
topScore: sorted[0]?.score || 0,
items: sorted.map(({ score, ...rest }) => rest)
};
};
const shouldFastReturn = (resultCount, topScore) => {
if (resultCount >= MIN_RESULTS_FAST_RETURN) {
return true;
}
return isSpecificQuery && resultCount >= 1 && topScore >= HIGH_CONFIDENCE_SCORE;
};
let mergedResults = dedupeResults([...animekaiData.page1Results]);
let rankedResults = rankResults(mergedResults);
let filteredResults = rankedResults.items;
if (shouldFastReturn(filteredResults.length, rankedResults.topScore)) {
return JSON.stringify(filteredResults);
}
const oneMoviesData = await oneMoviesSearch();
mergedResults = dedupeResults([
...mergedResults,
...oneMoviesData.page1Results
]);
const mergedResults = [...animekaiResults, ...oneMoviesResults];
rankedResults = rankResults(mergedResults);
filteredResults = rankedResults.items;
const scoredResults = mergedResults.map(r => ({
...r,
score: fuzzyMatch(query, r.title)
}));
if (shouldFastReturn(filteredResults.length, rankedResults.topScore)) {
return JSON.stringify(filteredResults);
}
const filteredResults = scoredResults
.filter(r => r.score > 50)
.sort((a, b) => b.score - a.score)
.map(({ score, ...rest }) => rest);
const [animekaiExtra, oneMoviesExtra] = await Promise.all([
animekaiData.fetchRemaining(),
oneMoviesData.fetchRemaining()
]);
mergedResults = dedupeResults([
...mergedResults,
...animekaiExtra,
...oneMoviesExtra
]);
rankedResults = rankResults(mergedResults);
filteredResults = rankedResults.items;
return JSON.stringify(filteredResults.length > 0 ? filteredResults : [{
href: "",
@@ -284,7 +431,8 @@ async function extractDetails(url) {
const actualUrl = url.replace("Animekai:", "").trim();
try {
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl));
const useProxy = await isAnimekaiBlockedForUser();
const response = await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl);
const htmlText = await response.text();
const descriptionMatch = (/<div class="desc text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
@@ -332,7 +480,8 @@ async function extractEpisodes(url) {
try {
if (url.startsWith("Animekai:")) {
const actualUrl = url.replace("Animekai:", "").trim();
const htmlText = await (await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl))).text();
const useProxy = await isAnimekaiBlockedForUser();
const htmlText = await (await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl)).text();
const animeIdMatch = (htmlText.match(/<div class="rate-box"[^>]*data-id="([^"]+)"/) || [])[1];
if (!animeIdMatch) return JSON.stringify([{ error: "AniID not found" }]);
@@ -341,7 +490,7 @@ async function extractEpisodes(url) {
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 episodeListData = await (await fetchv2(useProxy ? proxyUrl(episodeListUrl) : episodeListUrl)).json();
const cleanedHtml = cleanJsonHtml(episodeListData.result);
const episodeRegex = /<a[^>]+num="([^"]+)"[^>]+token="([^"]+)"[^>]*>/g;
@@ -398,6 +547,7 @@ async function extractStreamUrl(url) {
if (source === "Animekai") {
try {
const useProxy = await isAnimekaiBlockedForUser();
const tokenMatch = actualUrl.match(/token=([^&]+)/);
if (tokenMatch && tokenMatch[1]) {
const rawToken = tokenMatch[1];
@@ -407,7 +557,7 @@ async function extractStreamUrl(url) {
actualUrl = actualUrl.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
}
const response = await fetchv2("https://deno-proxies-sznvnpnxwhbv.deno.dev/?url=" + encodeURIComponent(actualUrl));
const response = await fetchv2(useProxy ? proxyUrl(actualUrl) : actualUrl);
const text = await response.text();
const cleanedHtml = cleanJsonHtml(text);
const subRegex = /<div class="server-items lang-group" data-id="sub"[^>]*>([\s\S]*?)<\/div>/;
@@ -454,7 +604,7 @@ async function extractStreamUrl(url) {
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 res = await fetchv2(useProxy ? proxyUrl(url) : url);
const json = await res.json();
return {
type: type,