mirror of
https://git.luna-app.eu/50n50/sources
synced 2025-12-21 21:26:19 +01:00
730 lines
No EOL
25 KiB
JavaScript
730 lines
No EOL
25 KiB
JavaScript
//
|
|
//
|
|
// Main functions
|
|
//
|
|
//
|
|
|
|
async function searchResults(query) {
|
|
const encodeQuery = keyword => encodeURIComponent(keyword);
|
|
|
|
const decodeHtmlEntities = (str) => {
|
|
if (!str) return str;
|
|
return str.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
|
|
.replace(/"/g, '"')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
};
|
|
|
|
const fuzzyMatch = (query, title) => {
|
|
const q = query.toLowerCase().trim();
|
|
const t = title.toLowerCase().trim();
|
|
|
|
if (t === q) return 1000;
|
|
|
|
if (t.startsWith(q + ' ') || t.startsWith(q + ':') || t.startsWith(q + '-')) return 950;
|
|
|
|
const wordBoundaryRegex = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
if (wordBoundaryRegex.test(t)) return 900;
|
|
|
|
const qTokens = q.split(/\s+/).filter(token => token.length > 0);
|
|
const tTokens = t.split(/[\s\-:]+/).filter(token => token.length > 0);
|
|
|
|
const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for', 'with']);
|
|
|
|
let score = 0;
|
|
let exactMatches = 0;
|
|
let partialMatches = 0;
|
|
let significantMatches = 0;
|
|
|
|
qTokens.forEach(qToken => {
|
|
const isStopword = stopwords.has(qToken);
|
|
let bestMatch = 0;
|
|
let hasExactMatch = false;
|
|
|
|
tTokens.forEach(tToken => {
|
|
let matchScore = 0;
|
|
|
|
if (tToken === qToken) {
|
|
matchScore = isStopword ? 25 : 120;
|
|
hasExactMatch = true;
|
|
if (!isStopword) significantMatches++;
|
|
}
|
|
else if (qToken.includes(tToken) && tToken.length >= 3 && qToken.length <= tToken.length + 2) {
|
|
matchScore = isStopword ? 8 : 40;
|
|
if (!isStopword) significantMatches++;
|
|
}
|
|
else if (tToken.startsWith(qToken) && qToken.length >= 3) {
|
|
matchScore = isStopword ? 12 : 70;
|
|
if (!isStopword) significantMatches++;
|
|
}
|
|
else if (qToken.length >= 4 && tToken.length >= 4) {
|
|
const dist = levenshteinDistance(qToken, tToken);
|
|
const maxLen = Math.max(qToken.length, tToken.length);
|
|
const similarity = 1 - (dist / maxLen);
|
|
|
|
if (similarity > 0.8) {
|
|
matchScore = Math.floor(similarity * 60);
|
|
if (!isStopword) significantMatches++;
|
|
}
|
|
}
|
|
|
|
bestMatch = Math.max(bestMatch, matchScore);
|
|
});
|
|
|
|
if (bestMatch > 0) {
|
|
score += bestMatch;
|
|
if (hasExactMatch) exactMatches++;
|
|
else partialMatches++;
|
|
}
|
|
});
|
|
|
|
const significantTokens = qTokens.filter(t => !stopwords.has(t)).length;
|
|
|
|
const requiredMatches = Math.max(1, Math.ceil(significantTokens * 0.8));
|
|
if (significantMatches < requiredMatches) {
|
|
return 0;
|
|
}
|
|
|
|
if (exactMatches + partialMatches >= qTokens.length) {
|
|
score += 80;
|
|
}
|
|
|
|
score += exactMatches * 20;
|
|
|
|
const extraWords = tTokens.length - qTokens.length;
|
|
if (extraWords > 2) {
|
|
score -= (extraWords - 2) * 25;
|
|
}
|
|
|
|
let orderBonus = 0;
|
|
for (let i = 0; i < qTokens.length - 1; i++) {
|
|
const currentTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i]));
|
|
const nextTokenIndex = tTokens.findIndex(t => t.includes(qTokens[i + 1]));
|
|
|
|
if (currentTokenIndex !== -1 && nextTokenIndex !== -1 && currentTokenIndex < nextTokenIndex) {
|
|
orderBonus += 15;
|
|
}
|
|
}
|
|
score += orderBonus;
|
|
|
|
return Math.max(0, score);
|
|
};
|
|
|
|
const levenshteinDistance = (a, b) => {
|
|
const matrix = [];
|
|
|
|
for (let i = 0; i <= b.length; i++) {
|
|
matrix[i] = [i];
|
|
}
|
|
|
|
for (let j = 0; j <= a.length; j++) {
|
|
matrix[0][j] = j;
|
|
}
|
|
|
|
for (let i = 1; i <= b.length; i++) {
|
|
for (let j = 1; j <= a.length; j++) {
|
|
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
matrix[i][j] = matrix[i - 1][j - 1];
|
|
} else {
|
|
matrix[i][j] = Math.min(
|
|
matrix[i - 1][j - 1] + 1,
|
|
matrix[i][j - 1] + 1,
|
|
matrix[i - 1][j] + 1
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return matrix[b.length][a.length];
|
|
};
|
|
|
|
const animekaiSearch = async () => {
|
|
const searchBaseUrl = "https://animekai.to/browser?keyword=";
|
|
const baseUrl = "https://animekai.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="([^"]*)"/;
|
|
|
|
const extractResultsFromHTML = (htmlText) => {
|
|
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 i = 0; i < minLength; i++) {
|
|
const hrefMatch = posterMatches[i].match(extractHrefRegex);
|
|
const fullHref = hrefMatch ? (hrefMatch[1].startsWith("http") ? hrefMatch[1] : baseUrl + hrefMatch[1]) : null;
|
|
|
|
const imageMatch = imageMatches[i].match(extractImageRegex);
|
|
const imageSrc = imageMatch ? imageMatch[1] : null;
|
|
|
|
const titleMatch = titleMatches[i].match(extractTitleRegex);
|
|
const cleanTitle = titleMatch ? decodeHtmlEntities(titleMatch[1]) : null;
|
|
|
|
if (fullHref && imageSrc && cleanTitle) {
|
|
results.push({
|
|
href: `Animekai:${fullHref}`,
|
|
image: imageSrc,
|
|
title: cleanTitle
|
|
});
|
|
}
|
|
}
|
|
|
|
return results;
|
|
};
|
|
|
|
try {
|
|
const encodedQuery = encodeQuery(query);
|
|
const urls = [
|
|
`${searchBaseUrl}${encodedQuery}`,
|
|
`${searchBaseUrl}${encodedQuery}&page=2`,
|
|
`${searchBaseUrl}${encodedQuery}&page=3`
|
|
];
|
|
|
|
const responses = await Promise.all(urls.map(url => fetchv2(url)));
|
|
const htmlTexts = await Promise.all(responses.map(res => res.text()));
|
|
|
|
const allResults = [];
|
|
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
|
|
return allResults;
|
|
} catch (error) {
|
|
console.error("Animekai search error:" + error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const oneMoviesSearch = async () => {
|
|
const searchBaseUrl = "https://1movies.bz/browser?keyword=";
|
|
const baseUrl = "https://1movies.bz";
|
|
|
|
const posterHrefRegex = /href="([^"]*)" class="poster"/g;
|
|
const titleRegex = /class="title" href="[^"]*">([^<]*)</g;
|
|
const imageRegex = /data-src="([^"]*)"/g;
|
|
|
|
const extractResultsFromHTML = (htmlText) => {
|
|
const results = [];
|
|
const posterMatches = [...htmlText.matchAll(posterHrefRegex)];
|
|
const titleMatches = [...htmlText.matchAll(titleRegex)];
|
|
const imageMatches = [...htmlText.matchAll(imageRegex)];
|
|
const minLength = Math.min(posterMatches.length, titleMatches.length, imageMatches.length);
|
|
|
|
for (let i = 0; i < minLength; i++) {
|
|
const href = posterMatches[i][1];
|
|
const fullHref = href.startsWith("http") ? href : baseUrl + href;
|
|
|
|
const imageSrc = imageMatches[i][1];
|
|
const title = decodeHtmlEntities(titleMatches[i][1]);
|
|
|
|
results.push({ href: fullHref, image: imageSrc, title });
|
|
}
|
|
return results;
|
|
};
|
|
|
|
try {
|
|
const encodedQuery = encodeQuery(query);
|
|
const urls = [
|
|
`${searchBaseUrl}${encodedQuery}`,
|
|
`${searchBaseUrl}${encodedQuery}&page=2`,
|
|
`${searchBaseUrl}${encodedQuery}&page=3`
|
|
];
|
|
|
|
const responses = await Promise.all(urls.map(url => fetchv2(url)));
|
|
const htmlTexts = await Promise.all(responses.map(res => res.text()));
|
|
|
|
const allResults = [];
|
|
htmlTexts.forEach(html => allResults.push(...extractResultsFromHTML(html)));
|
|
return allResults;
|
|
} catch (error) {
|
|
console.error("1Movies search error:" + error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
try {
|
|
const [animekaiResults, oneMoviesResults] = await Promise.all([
|
|
animekaiSearch(),
|
|
oneMoviesSearch()
|
|
]);
|
|
|
|
const mergedResults = [...animekaiResults, ...oneMoviesResults];
|
|
|
|
const scoredResults = mergedResults.map(r => ({
|
|
...r,
|
|
score: fuzzyMatch(query, r.title)
|
|
}));
|
|
|
|
const filteredResults = scoredResults
|
|
.filter(r => r.score > 50)
|
|
.sort((a, b) => b.score - a.score)
|
|
.map(({ score, ...rest }) => rest);
|
|
|
|
return JSON.stringify(filteredResults.length > 0 ? filteredResults : [{
|
|
href: "",
|
|
image: "",
|
|
title: "No results found, please refine query."
|
|
}]);
|
|
} catch (error) {
|
|
return JSON.stringify([{
|
|
href: "",
|
|
image: "",
|
|
title: "Search failed: " + error.message
|
|
}]);
|
|
}
|
|
}
|
|
|
|
async function extractDetails(url) {
|
|
|
|
if (url.startsWith("Animekai:")) {
|
|
const actualUrl = url.replace("Animekai:", "").trim();
|
|
|
|
try {
|
|
const response = await fetchv2(actualUrl);
|
|
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 Animekai details:" + error);
|
|
return JSON.stringify([{
|
|
description: "Error loading description",
|
|
aliases: "Aliases: Unknown",
|
|
airdate: "Aired: Unknown"
|
|
}]);
|
|
}
|
|
} else {
|
|
try {
|
|
const response = await fetchv2(url);
|
|
const htmlText = await response.text();
|
|
|
|
const descriptionMatch = (/<div class="description text-expand">([\s\S]*?)<\/div>/.exec(htmlText) || [])[1];
|
|
const aliasesMatch = (/<small class="al-title text-expand">([\s\S]*?)<\/small>/.exec(htmlText) || [])[1];
|
|
const airdateMatch = (/<li>Released:\s*<span[^>]*>(.*?)<\/span>/.exec(htmlText) || [])[1];
|
|
|
|
return JSON.stringify([{
|
|
description: descriptionMatch ? cleanHtmlSymbols(descriptionMatch) : "Not available",
|
|
aliases: aliasesMatch ? cleanHtmlSymbols(aliasesMatch) : "Not aliases",
|
|
airdate: airdateMatch ? cleanHtmlSymbols(airdateMatch) : "Not available"
|
|
}]);
|
|
} catch (error) {
|
|
console.error("Error fetching 1Movies details:"+ error);
|
|
return JSON.stringify([{
|
|
description: "Error loading description",
|
|
aliases: "Not available",
|
|
airdate: "Not available"
|
|
}]);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function extractEpisodes(url) {
|
|
try {
|
|
if (url.startsWith("Animekai:")) {
|
|
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://animekai.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: `Animekai:https://animekai.to/ajax/links/list?token=${episodeToken}&_=ENCRYPT_ME`
|
|
}));
|
|
|
|
return JSON.stringify(episodes);
|
|
} else {
|
|
const htmlText = await (await fetchv2(url)).text();
|
|
const movieIDMatch = (htmlText.match(/<div class="detail-lower"[^>]*id="movie-rating"[^>]*data-id="([^"]+)"/) || [])[1];
|
|
if (!movieIDMatch) return JSON.stringify([{ error: "MovieID not found" }]);
|
|
|
|
const tokenResponse = await fetchv2("https://enc-dec.app/api/enc-movies-flix?text=" + encodeURIComponent(movieIDMatch));
|
|
const temp = await tokenResponse.json();
|
|
const token = temp.result;
|
|
|
|
const episodeListUrl = `https://1movies.bz/ajax/episodes/list?id=${movieIDMatch}&_=${token}`;
|
|
const episodeListData = await (await fetchv2(episodeListUrl)).json();
|
|
const cleanedHtml = cleanJsonHtml(episodeListData.result);
|
|
|
|
const episodeRegex = /<a[^>]+eid="([^"]+)"[^>]+num="([^"]+)"[^>]*>/g;
|
|
const episodeMatches = [...cleanedHtml.matchAll(episodeRegex)];
|
|
|
|
const episodes = episodeMatches.map(([_, episodeToken, episodeNum]) => ({
|
|
number: parseInt(episodeNum, 10),
|
|
href: `https://1movies.bz/ajax/links/list?eid=${episodeToken}&_=ENCRYPT_ME`
|
|
}));
|
|
|
|
return JSON.stringify(episodes);
|
|
}
|
|
} catch (err) {
|
|
console.error("Error fetching episodes:" + err);
|
|
return JSON.stringify([{ number: 1, href: "Error fetching episodes" }]);
|
|
}
|
|
}
|
|
|
|
async function extractStreamUrl(url) {
|
|
let source, actualUrl;
|
|
|
|
if (url.startsWith("Animekai:")) {
|
|
source = "Animekai";
|
|
actualUrl = url.replace("Animekai:", "").trim();
|
|
} else if (url.includes("1movies.bz")) {
|
|
source = "1Movies";
|
|
actualUrl = url.trim();
|
|
} else {
|
|
console.log("Failed to match URL:", url);
|
|
return "Invalid URL format: " + url;
|
|
}
|
|
|
|
if (source === "Animekai") {
|
|
try {
|
|
const tokenMatch = actualUrl.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;
|
|
actualUrl = actualUrl.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
|
|
}
|
|
|
|
const response = await fetchv2(actualUrl);
|
|
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://animekai.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(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 decryptedSub = decryptedUrls.Sub;
|
|
const decryptedDub = decryptedUrls.Dub;
|
|
const decryptedRaw = decryptedUrls.Softsub;
|
|
|
|
const headers = {
|
|
"Referer": "https://animekai.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"
|
|
};
|
|
|
|
async function getStream(url) {
|
|
try {
|
|
const response = await fetchv2(url.replace("/e/", "/media/"), headers);
|
|
const responseJson = await response.json();
|
|
|
|
const result = responseJson?.result;
|
|
|
|
const postData = {
|
|
"text": result,
|
|
"Useragent": headers["User-Agent"]
|
|
};
|
|
|
|
const finalResponse = await fetchv2(
|
|
"https://ilovekai.simplepostrequest.workers.dev/ilovebush",
|
|
{},
|
|
"POST",
|
|
JSON.stringify(postData)
|
|
);
|
|
|
|
const finalJson = await finalResponse.json();
|
|
return finalJson?.result?.sources?.[0]?.file || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const streams = [];
|
|
|
|
const subStream = decryptedSub ? await getStream(decryptedSub) : null;
|
|
if (subStream) streams.push("Hardsub English", subStream);
|
|
|
|
const dubStream = decryptedDub ? await getStream(decryptedDub) : null;
|
|
if (dubStream) streams.push("Dubbed English", dubStream);
|
|
|
|
const rawStream = decryptedRaw ? await getStream(decryptedRaw) : null;
|
|
if (rawStream) streams.push("Original audio", rawStream);
|
|
|
|
const final = {
|
|
streams,
|
|
subtitles: ""
|
|
};
|
|
|
|
console.log("RETURN: " + JSON.stringify(final));
|
|
return JSON.stringify(final);
|
|
|
|
} catch (error) {
|
|
console.log("Animekai fetch error:" + error);
|
|
return "https://error.org";
|
|
}
|
|
} else if (source === "1Movies") {
|
|
try {
|
|
const eidMatch = actualUrl.match(/eid=([^&]+)/);
|
|
if (eidMatch && eidMatch[1]) {
|
|
const rawEpisodeToken = eidMatch[1];
|
|
const encryptResponse = await fetchv2(`https://enc-dec.app/api/enc-movies-flix?text=${rawEpisodeToken}`);
|
|
const encryptData = await encryptResponse.json();
|
|
const encryptedToken = encryptData.result;
|
|
actualUrl = actualUrl.replace('&_=ENCRYPT_ME', `&_=${encryptedToken}`);
|
|
}
|
|
|
|
const response = await fetchv2(actualUrl);
|
|
const responseData = await response.json();
|
|
const cleanedHtml = cleanJsonHtml(responseData.result);
|
|
|
|
const server1Regex = /<div class="server wnav-item"[^>]*data-lid="([^"]+)"[^>]*>\s*<span>Server 1<\/span>/;
|
|
const server1Match = server1Regex.exec(cleanedHtml);
|
|
|
|
if (!server1Match) {
|
|
console.log("Server 1 not found");
|
|
return "error";
|
|
}
|
|
|
|
const serverId = server1Match[1];
|
|
|
|
const tokenRequestData = [{ name: "Server1", data: serverId }];
|
|
|
|
const tokenBatchResponse = await fetchv2(
|
|
"https://ilovekai.simplepostrequest.workers.dev/ilovethighs",
|
|
{},
|
|
"POST",
|
|
JSON.stringify(tokenRequestData)
|
|
);
|
|
const tokenResults = await tokenBatchResponse.json();
|
|
const token = tokenResults[0]?.data;
|
|
|
|
if (!token) {
|
|
console.log("Token not found");
|
|
return "error";
|
|
}
|
|
|
|
const streamUrl = `https://1movies.bz/ajax/links/view?id=${serverId}&_=${token}`;
|
|
const streamResponse = await fetchv2(streamUrl);
|
|
const streamData = await streamResponse.json();
|
|
|
|
if (!streamData.result) {
|
|
console.log("Stream result not found");
|
|
return "error";
|
|
}
|
|
|
|
const decryptRequestData = [{ name: "Server1", data: streamData.result }];
|
|
|
|
const decryptBatchResponse = await fetchv2(
|
|
"https://ilovekai.simplepostrequest.workers.dev/iloveboobs",
|
|
{},
|
|
"POST",
|
|
JSON.stringify(decryptRequestData)
|
|
);
|
|
const decryptedResponse = await decryptBatchResponse.json();
|
|
console.log("Decrypted response:" + JSON.stringify(decryptedResponse));
|
|
const decryptedUrl = decryptedResponse[0]?.data.url;
|
|
|
|
const subListEncoded = decryptedUrl.split("sub.list=")[1]?.split("&")[0];
|
|
let subtitles = "N/A";
|
|
|
|
if (subListEncoded) {
|
|
try {
|
|
const subListUrl = decodeURIComponent(subListEncoded);
|
|
const subResponse = await fetchv2(subListUrl);
|
|
subtitles = await subResponse.json();
|
|
} catch {
|
|
subtitles = "N/A";
|
|
}
|
|
}
|
|
|
|
const englishSubUrl = Array.isArray(subtitles)
|
|
? subtitles.find(sub => sub.label === "English")?.file.replace(/\\\//g, "/")
|
|
: "N/A";
|
|
|
|
if (!decryptedUrl) {
|
|
console.log("Decryption failed");
|
|
return "error";
|
|
}
|
|
|
|
const headers = {
|
|
"Referer": "https://1movies.bz/",
|
|
"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"
|
|
};
|
|
|
|
const mediaResponse = await fetchv2(decryptedUrl.replace("/e/", "/media/"), headers);
|
|
const mediaJson = await mediaResponse.json();
|
|
|
|
const result = mediaJson?.result;
|
|
if (!result) {
|
|
console.log("Media result not found");
|
|
return "error";
|
|
}
|
|
|
|
const postData = {
|
|
"text": result,
|
|
"Useragent": "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://ilovekai.simplepostrequest.workers.dev/iloveass", {}, "POST", JSON.stringify(postData));
|
|
const finalJson = await finalResponse.json();
|
|
|
|
const m3u8Link = finalJson?.result?.sources?.[0]?.file;
|
|
|
|
const returnValue = {
|
|
stream: m3u8Link,
|
|
subtitles: englishSubUrl
|
|
};
|
|
console.log("RETURN: " + JSON.stringify(returnValue));
|
|
return JSON.stringify(returnValue);
|
|
} catch (error) {
|
|
console.log("1Movies fetch error:" + error);
|
|
return "https://error.org";
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
///
|
|
/// Helper functions
|
|
///
|
|
///
|
|
|
|
function cleanHtmlSymbols(string) {
|
|
if (!string) {
|
|
return "";
|
|
}
|
|
return string
|
|
.replace(/’/g, "'")
|
|
.replace(/–/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(/'/g, "'")
|
|
.replace(/"/g, "\"")
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/ /g, " ");
|
|
} |