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 = /]*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: encodeURIComponent(`${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: encodeURIComponent(`${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; } } }