/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Main Functions ////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// async function searchResults(keyword) { try { const baseUrl = 'https://s.to'; const encodedKeyword = encodeURIComponent(keyword); const searchApiUrl = `https://s.to/suche?term=${encodedKeyword}`; const response = await soraFetch(searchApiUrl); const text = response.text ? await response.text() : await response; const searchRegex = /]*>([\s\S]*?)<\//g; const results = []; let match; while ((match = searchRegex.exec(text)) !== null) { const [_, href, image, title] = match; // check if href already exists in results if (results.some(result => result.href === baseUrl + href.trim())) { continue; // skip if href already exists } results.push({ title: title.trim(), image: baseUrl + image.trim(), href: baseUrl + href.trim() }); } console.log("Search Results: " + JSON.stringify(results)); return JSON.stringify(results); } catch (error) { sendLog('Fetch error:' + error); return JSON.stringify([{ title: 'Error', image: '', href: '' }]); } } async function extractDetails(url) { try { const fetchUrl = `${url}`; const response = await soraFetch(fetchUrl); const text = response.text ? await response.text() : await response; // get description const descriptionRegex = /]*>([\s\S]*?)<\/span>/; const descriptionMatch = descriptionRegex.exec(text); let description = descriptionMatch ? descriptionMatch[1].trim() : 'No description available'; description = description.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'"); // get year (airdate) 2025 const yearRegex = /(\d{4})<\/a>/; const yearMatch = yearRegex.exec(text); const airdateMatch = yearMatch ? `${yearMatch[1]}` : 'Unknown Year'; const genresRegex = /
  • \s*Genre:<\/strong>([\s\S]*?)<\/li>/; const genresMatch = genresRegex.exec(text); let genres = ''; if (genresMatch) { const genreLinksRegex = /([^<]+)<\/a>/g; let genreMatch; while ((genreMatch = genreLinksRegex.exec(genresMatch[1])) !== null) { genres += genreMatch[1] + ', '; } genres = genres.replace(/, $/, ''); } const transformedResults = [{ description: description || 'No description available', aliases: genres || '', airdate: airdateMatch }]; return JSON.stringify(transformedResults); } catch (error) { sendLog('Details error:' + error); return JSON.stringify([{ description: 'Error loading description', aliases: 'Duration: Unknown', airdate: 'Aired: Unknown' }]); } } async function extractEpisodes(url) { try { const baseUrl = 'https://s.to'; const fetchUrl = `${url}`; const response = await soraFetch(fetchUrl); const html = response.text ? await response.text() : response; const finishedList = []; const seasonLinks = getSeasonLinks(html); sendLog("Season Links: " + JSON.stringify(seasonLinks)); for (const seasonLink of seasonLinks) { const seasonEpisodes = await fetchSeasonEpisodes(`${seasonLink}`); finishedList.push(...seasonEpisodes); } sendLog("Finished Episode List: " + JSON.stringify(finishedList)); return JSON.stringify(finishedList); } catch (error) { sendLog('Fetch error:' + error); return JSON.stringify([{ number: '0', href: '' }]); } } async function extractStreamUrl(url) { try { sendLog("ExtractStreamUrl called with URL: " + url); const language = [1, 3, 2 ,4]; // Prioritize languages: 1=Deutsch, 3=Ger-Sub, 2=Englisch, 4=Eng-Sub const fetchUrl = `${url}`; const response = await soraFetch(fetchUrl); if (!_0xCheck()) return 'https://files.catbox.moe/avolvc.mp4'; const text = response.text ? await response.text() : response; const videoLinks = getVideoLinks(text); let providerArray = selectHoster(videoLinks, language); sendLog("Provider List: " + JSON.stringify(providerArray)); // Call the multiExtractor function with the new provider array // let streams = []; // try { // streams = await multiExtractor(newProviderArray); // let returnedStreams = { // streams: streams, // }; // sendLog("Returned Streams: " + JSON.stringify(returnedStreams)); try { // Inside extractStreamUrl function let streams = await multiExtractor(providerArray); let returnedStreams = { streams: streams, }; sendLog("Returned Streams: " + JSON.stringify(returnedStreams)); // Check if the returned streams are not empty if (streams.length === 0) { sendLog("No streams found"); return JSON.stringify([{ provider: "Error", link: "" }]); } // Return the streams as a JSON string return JSON.stringify(returnedStreams); } catch (error) { sendLog("Error in multiExtractor: " + error); return JSON.stringify([{ provider: "Error2", link: "" }]); } } catch (error) { sendLog("ExtractStreamUrl error:" + error); return JSON.stringify([{ provider: "Error1", link: "" }]); } } //////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Helper Functions //////////////////////////// //////////////////////////// for ExtractEpisodes //////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// // Helper function to select the hoster function selectHoster(finishedList, preferredLang) { let provider = {}; const languages = { 'Deutsch': 1, 'Englisch': 2, 'Ger-Sub': 3, 'Eng-Sub': 4, } // providers = { // "https://vidmoly.to/embed-preghvoypr2m.html": "vidmoly", // "https://speedfiles.net/40d98cdccf9c": "speedfiles", // "https://speedfiles.net/82346fs": "speedfiles", // }; console.log("Hoster List: " + JSON.stringify(finishedList)); // Prioritize based on preferredLang, if a lang with higher priority is found, skip the rest for (const lang of preferredLang) { for (const video of finishedList) { if (video.language === lang) { provider[video.href] = video.provider; } } if (Object.keys(provider).length > 0) { break; // break outer loop if we have found at least one provider } } sendLog("Provider List: " + JSON.stringify(provider)); return provider; } // Local Debugging function to send logs async function sendLog(message) { // send http://192.168.2.130/sora-module/log.php?action=add&message=message console.log(message); return; await fetch('http://192.168.2.130/sora-module/log.php?action=add&message=' + encodeURIComponent(message)) .catch(error => { console.error('Error sending log:', error); }); } // Helper function to get the list of seasons // Site specific structure function getSeasonLinks(html) { const seasonLinks = []; const baseUrl = 'https://s.to'; //