diff --git a/s.to/sToEngDub.json b/s.to/sToEngDub.json index ebac88e..63672a3 100644 --- a/s.to/sToEngDub.json +++ b/s.to/sToEngDub.json @@ -2,10 +2,11 @@ "sourceName": "s.to (ENG DUB)", "iconUrl": "https://git.luna-app.eu/50n50/sources/raw/branch/main/s.to/sto.png", "author": { - "name": "Cufiy", - "icon": "https://files.catbox.moe/ttj4fc.gif" + "name": "Cufiy & Hamzo", + "icon": "https://files.catbox.moe/ttj4fc.gif", + "url": "https://github.com/JMcrafter26" }, - "version": "0.3.17", + "version": "0.4.0", "language": "English (DUB)", "streamType": "HLS", "quality": "720p", diff --git a/s.to/sToEngDub_v2.js b/s.to/sToEngDub_v2.js index 66a7e75..3d6bef3 100644 --- a/s.to/sToEngDub_v2.js +++ b/s.to/sToEngDub_v2.js @@ -5,19 +5,71 @@ async function searchResults(keyword) { try { const encodedKeyword = encodeURIComponent(keyword); - const searchApiUrl = `https://s.to/ajax/seriesSearch?keyword=${encodedKeyword}`; + const searchApiUrl = `https://s.to/suche?term=${encodedKeyword}`; const response = await soraFetch(searchApiUrl); - const responseText = await response?.text() ?? response; + const text = response.text ? await response.text() : await response; - const data = await JSON.parse(responseText); + // parse html + /*
+ +
+ + - const transformedResults = data.map(serie => ({ - title: serie.name, - image: `https://s.to${serie.cover}`, - href: `https://s.to/serie/stream/${serie.link}` - })); + - return JSON.stringify(transformedResults); + + + + + Pluribus - Glück ist ansteckend + + + + + +
+
Pluribus - Glück ist + ansteckend
+
+
+ +
+ ... + */ + 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 === href.trim())) { + continue; + } + + results.push({ title: title.trim(), image: image.trim(), href: href.trim() }); + } + + + + console.log("Search Results: " + JSON.stringify(results)); + return JSON.stringify(results); } catch (error) { sendLog('Fetch error:' + error); @@ -29,31 +81,49 @@ async function extractDetails(url) { try { const fetchUrl = `${url}`; const response = await soraFetch(fetchUrl); - const text = response.text ? await response.text() : response; + 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, "'"); - const descriptionRegex = /(.*?)<\/p>/s; - const aliasesRegex = /]*\bdata-alternativetitles="([^"]+)"[^>]*>/i; + // get year (airdate) 2025 + const yearRegex = /(\d{4})<\/a>/; + const yearMatch = yearRegex.exec(text); + const airdateMatch = yearMatch ? `${yearMatch[1]}` : 'Unknown Year'; - const aliasesMatch = aliasesRegex.exec(text); - let aliasesArray = []; - if (aliasesMatch) { - aliasesArray = aliasesMatch[1].split(',').map(a => a.trim()); + + // get genres + /*
  • + Genre: + + + Science Fiction, Comedy, Drama + +
  • + */ + 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 descriptionMatch = descriptionRegex.exec(text) || []; - // sanitize description by removing HTML tags - let description = descriptionMatch[1] || ''; - description = description.replace(/<[^>]+>/g, '').trim(); - - - const airdateMatch = "Unknown"; // TODO: Implement airdate extraction const transformedResults = [{ description: description || 'No description available', - aliases: aliasesArray[0] || 'No aliases available', + aliases: genres || '', airdate: airdateMatch }]; + return JSON.stringify(transformedResults); } catch (error) { sendLog('Details error:' + error); @@ -77,15 +147,11 @@ async function extractEpisodes(url) { console.log("Season Links: " + JSON.stringify(seasonLinks)); for (const seasonLink of seasonLinks) { - const seasonEpisodes = await fetchSeasonEpisodes(`${baseUrl}${seasonLink}`); + const seasonEpisodes = await fetchSeasonEpisodes(`${seasonLink}`); finishedList.push(...seasonEpisodes); } console.log("Finished Episode List: " + JSON.stringify(finishedList)); - // Replace the field "number" with the current index of each item, starting from 1 - finishedList.forEach((item, index) => { - item.number = index + 1; - }); return JSON.stringify(finishedList); @@ -99,54 +165,19 @@ async function extractEpisodes(url) { async function extractStreamUrl(url) { try { - const baseUrl = 'https://s.to'; + const language = [2, 4, 3]; // Englisch Dub, Eng-Sub, Ger-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 finishedList = []; - const languageList = getAvailableLanguages(text); const videoLinks = getVideoLinks(text); - if (!_0xCheck()) return 'https://files.catbox.moe/avolvc.mp4'; - sendLog("Video Links: " + JSON.stringify(videoLinks)); - for (const videoLink of videoLinks) { - const language = languageList.find( - (l) => l.langKey === videoLink.langKey - ); - if (language) { - finishedList.push({ - provider: videoLink.provider, - href: `${baseUrl}${videoLink.href}`, - language: language.title, - }); - } - } - // Select the hoster - let providerArray = selectHoster(finishedList); - let newProviderArray = {}; + let providerArray = selectHoster(videoLinks, language); - for (const [key, value] of Object.entries(providerArray)) { - const providerLink = key; - const providerName = value; - - // fetch the provider link and extract the stream URL - const streamUrl = await soraFetch(providerLink); - console.log("Stream URL: " + streamUrl); - const winLocRegex = /window\.location\.href\s*=\s*['"]([^'"]+)['"]/; - const winLocMatch = await winLocRegex.exec(streamUrl); - let winLocUrl = null; - if (!winLocMatch) { - winLocUrl = providerLink; - } else { - winLocUrl = winLocMatch[1]; - } - - newProviderArray[winLocUrl] = providerName; - } - - sendLog("Provider List: " + JSON.stringify(newProviderArray)); + sendLog("Provider List: " + JSON.stringify(providerArray)); // Call the multiExtractor function with the new provider array // let streams = []; @@ -159,7 +190,7 @@ async function extractStreamUrl(url) { try { // Inside extractStreamUrl function - let streams = await multiExtractor(newProviderArray); + let streams = await multiExtractor(providerArray); let returnedStreams = { streams: streams, }; @@ -194,8 +225,14 @@ async function extractStreamUrl(url) { ///////////////////////////////////////////////////////////////////////////////////// // Helper function to select the hoster -function selectHoster(finishedList) { +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", @@ -204,26 +241,18 @@ function selectHoster(finishedList) { console.log("Hoster List: " + JSON.stringify(finishedList)); - // Define the preferred providers and languages - const providerList = ["VOE", "SpeedFiles", "Filemoon", "Vidmoly", "DoodStream", "Vidoza", "MP4Upload"]; - const languageList = ["English", "mit Untertitel Deutsch", "mit Untertitel Englisch"]; - - - - for (const language of languageList) { - for (const providerName of providerList) { - const video = finishedList.find( - (video) => video.provider === providerName && video.language === language - ); - if (video) { - provider[video.href] = providerName.toLowerCase(); + // 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 } } - // if the array is not empty, break the loop - if (Object.keys(provider).length > 0) { - break; - } - } + sendLog("Provider List: " + JSON.stringify(provider)); return provider; @@ -246,11 +275,24 @@ async function sendLog(message) { // Site specific structure function getSeasonLinks(html) { const seasonLinks = []; - const seasonRegex = /
    .*?