324 lines
13 KiB
JavaScript
324 lines
13 KiB
JavaScript
async function searchResults(keyword) {
|
|
try {
|
|
let transformedResults = [];
|
|
|
|
const encodedKeyword = encodeURIComponent(keyword);
|
|
const apiUrl = `https://thedatabase-brown.vercel.app/api/search?q=${encodedKeyword}&limit=50`;
|
|
|
|
const response = await soraFetch(apiUrl);
|
|
const data = await response.json();
|
|
|
|
let dataResults = data.results || [];
|
|
|
|
if (dataResults.length > 0) {
|
|
transformedResults = dataResults
|
|
.map(result => {
|
|
const isMovie = result.url.includes('/movies/');
|
|
const isTV = result.url.includes('/tvs/');
|
|
|
|
return {
|
|
title: result.title || "Untitled",
|
|
image: result.poster_path || "",
|
|
href: isMovie ? `movie/${result.tmdb_id}` : isTV ? `tv/${result.tmdb_id}/1/1` : "",
|
|
};
|
|
})
|
|
}
|
|
|
|
console.log("Transformed Results: " + JSON.stringify(transformedResults));
|
|
return JSON.stringify(transformedResults);
|
|
} catch (error) {
|
|
console.log("Fetch error in searchResults: " + error);
|
|
return JSON.stringify([{ title: "Error", image: "", href: "" }]);
|
|
}
|
|
}
|
|
|
|
async function extractDetails(url) {
|
|
try {
|
|
if(url.includes('movie')) {
|
|
const match = url.match(/movie\/([^\/]+)/);
|
|
if (!match) throw new Error("Invalid URL format");
|
|
|
|
const movieId = match[1];
|
|
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/movie/${movieId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
|
|
const data = await responseText.json();
|
|
|
|
const transformedResults = [{
|
|
description: data.overview || 'No description available',
|
|
aliases: `Duration: ${data.runtime ? data.runtime + " minutes" : 'Unknown'}`,
|
|
airdate: `Released: ${data.release_date ? data.release_date : 'Unknown'}`
|
|
}];
|
|
|
|
return JSON.stringify(transformedResults);
|
|
} else if(url.includes('tv')) {
|
|
const match = url.match(/tv\/([^\/]+)/);
|
|
if (!match) throw new Error("Invalid URL format");
|
|
|
|
const showId = match[1];
|
|
const responseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
|
|
const data = await responseText.json();
|
|
|
|
const transformedResults = [{
|
|
description: data.overview || 'No description available',
|
|
aliases: `Duration: ${data.episode_run_time && data.episode_run_time.length ? data.episode_run_time.join(', ') + " minutes" : 'Unknown'}`,
|
|
airdate: `Aired: ${data.first_air_date ? data.first_air_date : 'Unknown'}`
|
|
}];
|
|
|
|
console.log(JSON.stringify(transformedResults));
|
|
return JSON.stringify(transformedResults);
|
|
} else {
|
|
throw new Error("Invalid URL format");
|
|
}
|
|
} catch (error) {
|
|
console.log('Details error: ' + error);
|
|
return JSON.stringify([{
|
|
description: 'Error loading description',
|
|
aliases: 'Duration: Unknown',
|
|
airdate: 'Aired/Released: Unknown'
|
|
}]);
|
|
}
|
|
}
|
|
|
|
async function extractEpisodes(url) {
|
|
try {
|
|
if(url.includes('movie')) {
|
|
const match = url.match(/movie\/([^\/]+)/);
|
|
|
|
if (!match) throw new Error("Invalid URL format");
|
|
|
|
const movieId = match[1];
|
|
|
|
const movie = [
|
|
{ href: `/movie/${movieId}`, number: 1, title: "Full Movie" }
|
|
];
|
|
|
|
console.log(movie);
|
|
return JSON.stringify(movie);
|
|
} else if(url.includes('tv')) {
|
|
const match = url.match(/tv\/([^\/]+)\/([^\/]+)\/([^\/]+)/);
|
|
|
|
if (!match) throw new Error("Invalid URL format");
|
|
|
|
const showId = match[1];
|
|
|
|
const showResponseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
|
|
const showData = await showResponseText.json();
|
|
|
|
let allEpisodes = [];
|
|
for (const season of showData.seasons) {
|
|
const seasonNumber = season.season_number;
|
|
|
|
if(seasonNumber === 0) continue;
|
|
|
|
const seasonResponseText = await soraFetch(`https://post-eosin.vercel.app/api/proxy?url=${encodeURIComponent(`https://api.themoviedb.org/3/tv/${showId}/season/${seasonNumber}?api_key=ad301b7cc82ffe19273e55e4d4206885`)}&simple=true`);
|
|
const seasonData = await seasonResponseText.json();
|
|
|
|
if (seasonData.episodes && seasonData.episodes.length) {
|
|
const episodes = seasonData.episodes.map(episode => ({
|
|
href: `/tv/${showId}/${seasonNumber}/${episode.episode_number}`,
|
|
number: episode.episode_number,
|
|
title: episode.name || ""
|
|
}));
|
|
allEpisodes = allEpisodes.concat(episodes);
|
|
}
|
|
}
|
|
|
|
console.log(allEpisodes);
|
|
return JSON.stringify(allEpisodes);
|
|
} else {
|
|
throw new Error("Invalid URL format");
|
|
}
|
|
} catch (error) {
|
|
console.log('Fetch error in extractEpisodes: ' + error);
|
|
return JSON.stringify([]);
|
|
}
|
|
}
|
|
|
|
async function extractStreamUrl(ID) {
|
|
const parts = ID.split('/').filter(Boolean);
|
|
const type = parts[0];
|
|
const id = parts[1];
|
|
const season = parts[2];
|
|
const episode = parts[3];
|
|
|
|
try {
|
|
if (type === 'movie') {
|
|
const response = await soraFetch(`https://thedatabase-brown.vercel.app/api/search?tmdb=${id}`);
|
|
const data = await response.json();
|
|
|
|
const baseUrl = data.results[0].full_url;
|
|
console.log("Base URL: " + baseUrl);
|
|
|
|
const htmlResponse = await soraFetch(baseUrl);
|
|
const htmlText = htmlResponse ? await htmlResponse.text() : "";
|
|
|
|
if (htmlText.includes('Error') && htmlText.includes('1015') && htmlText.includes('rate limited')) {
|
|
return JSON.stringify({
|
|
streams: [{
|
|
title: "Rate Limited - Please slow down and try again later",
|
|
streamUrl: "",
|
|
headers: {}
|
|
}],
|
|
subtitle: "None"
|
|
});
|
|
}
|
|
|
|
const fileRegex1 = /<tr data-entry="true"[^>]*data-name="([^"]+)"[^>]*data-url="([^"]+)"[\s\S]*?data-sort="(\d+)"/g;
|
|
const fileRegex2 = /data-name="([^"]+)"[^>]*data-url="([^"]+)"[\s\S]*?data-sort="(\d+)"/g;
|
|
|
|
let matches = Array.from(htmlText.matchAll(fileRegex1));
|
|
console.log("Regex1 matches: " + matches.length);
|
|
|
|
if (matches.length === 0) {
|
|
matches = Array.from(htmlText.matchAll(fileRegex2));
|
|
console.log("Regex2 matches: " + matches.length);
|
|
}
|
|
|
|
const streams = [];
|
|
|
|
for (const match of matches) {
|
|
const filename = match[1];
|
|
const fileUrl = match[2];
|
|
const sizeBytes = parseInt(match[3]);
|
|
const sizeGB = (sizeBytes / 1073741824).toFixed(1);
|
|
|
|
const fileExt = filename.split('.').pop().toUpperCase();
|
|
const isValidType = fileExt === 'MP4' || fileExt === 'MKV';
|
|
|
|
console.log(`File: ${filename}, Ext: ${fileExt}, Valid: ${isValidType}`);
|
|
|
|
if (isValidType) {
|
|
const cleanName = filename.replace(/\./g, ' ').replace(/\.[^.]+$/, '').trim();
|
|
|
|
streams.push({
|
|
title: `[${fileExt}] [${sizeGB} GB] ${cleanName}`,
|
|
streamUrl: `${baseUrl}${filename}`,
|
|
headers: {},
|
|
sizeGB: parseFloat(sizeGB)
|
|
});
|
|
}
|
|
}
|
|
|
|
streams.sort((a, b) => a.sizeGB - b.sizeGB);
|
|
|
|
streams.forEach(stream => delete stream.sizeGB);
|
|
|
|
const result = {
|
|
streams: streams,
|
|
subtitle: "None"
|
|
};
|
|
|
|
console.log("Extracted streams: " + JSON.stringify(result));
|
|
return JSON.stringify(result);
|
|
} else if (type === 'tv') {
|
|
const response = await soraFetch(`https://thedatabase-brown.vercel.app/api/search?tmdb=${id}`);
|
|
const data = await response.json();
|
|
|
|
const baseUrl = data.results[0].full_url;
|
|
const seasonListHtml = await (await soraFetch(baseUrl)).text();
|
|
|
|
if (seasonListHtml.includes('Error') && seasonListHtml.includes('1015') && seasonListHtml.includes('rate limited')) {
|
|
return JSON.stringify({
|
|
streams: [{
|
|
title: "Rate Limited - Please slow down and try again later",
|
|
streamUrl: "",
|
|
headers: {}
|
|
}],
|
|
subtitle: "None"
|
|
});
|
|
}
|
|
|
|
if (!seasonListHtml) {
|
|
return JSON.stringify({ streams: [], subtitle: "None" });
|
|
}
|
|
|
|
const seasonRegex = new RegExp(`data-name="Season\\s+${season}"[^>]*data-url="([^"]+)"`, 'i');
|
|
const seasonMatch = seasonListHtml.match(seasonRegex);
|
|
|
|
if (!seasonMatch) {
|
|
return JSON.stringify({ streams: [], subtitle: "None" });
|
|
}
|
|
|
|
const domain = baseUrl.split('/').slice(0, 3).join('/');
|
|
const seasonUrl = `${domain}${seasonMatch[1]}`;
|
|
|
|
const episodeListHtml = await (await soraFetch(seasonUrl)).text();
|
|
|
|
if (episodeListHtml.includes('Error') && episodeListHtml.includes('1015') && episodeListHtml.includes('rate limited')) {
|
|
return JSON.stringify({
|
|
streams: [{
|
|
title: "Rate Limited - Please slow down and try again later",
|
|
streamUrl: "",
|
|
headers: {}
|
|
}],
|
|
subtitle: "None"
|
|
});
|
|
}
|
|
|
|
if (!episodeListHtml) {
|
|
return JSON.stringify({ streams: [], subtitle: "None" });
|
|
}
|
|
|
|
const episodePadded = String(episode).padStart(2, '0');
|
|
const seasonPadded = String(season).padStart(2, '0');
|
|
const episodePattern = `S${seasonPadded}E${episodePadded}`;
|
|
|
|
const fileRegex = new RegExp(`data-name="([^"]*${episodePattern}[^"]*)"[^>]*data-url="([^"]+)"[\\s\\S]*?data-sort="(\\d+)"`, 'gi');
|
|
const matches = Array.from(episodeListHtml.matchAll(fileRegex));
|
|
|
|
const streams = [];
|
|
|
|
for (const match of matches) {
|
|
const filename = match[1];
|
|
const sizeBytes = parseInt(match[3]);
|
|
const sizeGB = (sizeBytes / 1073741824).toFixed(1);
|
|
|
|
const fileExt = filename.split('.').pop().toUpperCase();
|
|
|
|
if (fileExt === 'MP4' || fileExt === 'MKV') {
|
|
const cleanName = filename.replace(/\./g, ' ').replace(/\.[^.]+$/, '').trim();
|
|
|
|
streams.push({
|
|
title: `[${fileExt}] [${sizeGB} GB] ${cleanName}`,
|
|
streamUrl: `${seasonUrl}${filename}`,
|
|
headers: {},
|
|
sizeGB: parseFloat(sizeGB)
|
|
});
|
|
}
|
|
}
|
|
|
|
streams.sort((a, b) => a.sizeGB - b.sizeGB);
|
|
streams.forEach(stream => delete stream.sizeGB);
|
|
|
|
return JSON.stringify({
|
|
streams: streams,
|
|
subtitle: "None"
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.log('Fetch error in extractStreamUrl: ' + error);
|
|
return JSON.stringify({
|
|
streams: [],
|
|
subtitle: "None"
|
|
});
|
|
}
|
|
}
|
|
|
|
async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) {
|
|
try {
|
|
return await fetchv2(
|
|
url,
|
|
options.headers ?? {},
|
|
options.method ?? 'GET',
|
|
options.body ?? null,
|
|
true,
|
|
options.encoding ?? 'utf-8'
|
|
);
|
|
} catch(e) {
|
|
try {
|
|
return await fetch(url, options);
|
|
} catch(error) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|