Add jiao/jiao.js
This commit is contained in:
+323
@@ -0,0 +1,323 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user