Update ashi/ashi.js
This commit is contained in:
+190
-40
@@ -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 & 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,
|
||||
|
||||
Reference in New Issue
Block a user