diff --git a/checkmate/checkmate.js b/checkmate/checkmate.js index b7dde4d..bd47adb 100644 --- a/checkmate/checkmate.js +++ b/checkmate/checkmate.js @@ -188,10 +188,11 @@ async function extractStreamUrl(ID) { const betaPromise = beta(ID).then(result => ({ source: 'beta', data: JSON.parse(result) })).catch(err => ({ source: 'beta', error: err })); const gammaPromise = gamma(ID).then(result => ({ source: 'gamma', data: JSON.parse(result) })).catch(err => ({ source: 'gamma', error: err })); const deltaPromise = delta(ID).then(result => ({ source: 'delta', data: JSON.parse(result) })).catch(err => ({ source: 'delta', error: err })); + const epsilonPromise = epsilon(ID).then(result => ({ source: 'epsilon', data: JSON.parse(result) })).catch(err => ({ source: 'epsilon', error: err })); const racePromise = Promise.race([alphaPromise, gammaPromise, deltaPromise]); - const [fastestResult, betaResult, alphaResult] = await Promise.all([racePromise, betaPromise, alphaPromise]); + const [fastestResult, betaResult, alphaResult, epsilonResult] = await Promise.all([racePromise, betaPromise, alphaPromise, epsilonPromise]); const resultStreams = []; let subtitle = null; @@ -242,6 +243,28 @@ async function extractStreamUrl(ID) { console.log('Alpha did not return a 4K stream'); } } + + if (epsilonResult && !epsilonResult.error && epsilonResult.data && epsilonResult.data.streams) { + const streams = epsilonResult.data.streams; + let fourKUrl = null; + + for (const stream of streams) { + if (stream.title && stream.title.includes('4K') && stream.streamUrl) { + fourKUrl = stream; + break; + } + } + + if (fourKUrl) { + resultStreams.push({ + title: "4K Secondary", + streamUrl: fourKUrl.streamUrl, + headers: fourKUrl.headers || { "Referer": "https://mapple.uk/", "Origin": "https://mapple.uk" } + }); + } else { + console.log('Epsilon did not return a 4K stream'); + } + } console.log(JSON.stringify({ streams: resultStreams, subtitles: subtitle @@ -281,7 +304,7 @@ async function soraFetch(url, options = { headers: {}, method: 'GET', body: null async function alpha(ID) { if (ID.includes('movie')) { const tmdbID = ID.replace('/movie/', ''); - const cinebyResponse = await soraFetch(`https://jumpfreedom.com/3/movie/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`); + const cinebyResponse = await soraFetch(`https://db.videasy.net/3/movie/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`); const cinebyData = await cinebyResponse.json(); const title = encodeURIComponent(cinebyData.title); @@ -333,7 +356,7 @@ async function alpha(ID) { const seasonNumber = parts[3]; const episodeNumber = parts[4]; - const cinebyResponse = await soraFetch(`https://jumpfreedom.com/3/tv/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`); + const cinebyResponse = await soraFetch(`hhttps://db.videasy.net/3/tv/${tmdbID}?append_to_response=external_ids&language=en&api_key=ad301b7cc82ffe19273e55e4d4206885`); const cinebyData = await cinebyResponse.json(); const title = encodeURIComponent(cinebyData.name); @@ -610,6 +633,202 @@ async function delta(ID) { } } +async function epsilon(ID) { + const actionIdentifier = await ilovethighs("https://mapple.uk/watch/movie/1061474-superman"); + + console.log("Action Identifier: " + actionIdentifier); + + const sessionResponse = await soraFetch("https://enc-dec.app/api/enc-mapple"); + const sessionData = await sessionResponse.json(); + console.log("Session Data: " + JSON.stringify(sessionData)); + const sessionID = sessionData.result.sessionId || "N/A"; + console.log("ID: " + ID); + if (ID.startsWith('/movie/') || ID.startsWith('movie/')) { + const match = ID.match(/movie\/(\d+)/); + const tmdbID = match ? match[1] : ''; + console.log("Extracted TMDB ID: " + tmdbID); + const headers = { + "Content-Type": "text/plain", + "next-action": actionIdentifier + }; + const postData = `[{"mediaId":${tmdbID},"mediaType":"movie","tv_slug":"","source":"mapple","useFallbackVideo":false,"sessionId":"${sessionID}"}]`; + const response = await fetchv2("https://mapple.uk/watch/"+ ID, headers, "POST", postData); + const data = await response.text(); + console.log("Stream data: " + data); + + const lines = data.split('\n'); + let streamData = null; + for (const line of lines) { + if (line.includes('"success":true')) { + streamData = JSON.parse(line.substring(line.indexOf('{'))); + break; + } + } + + if (streamData && streamData.data && streamData.data.stream_url) { + const m3u8Response = await soraFetch(streamData.data.stream_url, { + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + }); + const m3u8Text = await m3u8Response.text(); + console.log("M3U8 Playlist: " + m3u8Text); + + const streams = []; + const lines = m3u8Text.split('\n'); + const resolutionNames = { + "3840x2160": "4K", + "1920x1080": "1080p", + "1280x720": "720p", + "640x360": "360p" + }; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith('#EXT-X-STREAM-INF')) { + const resolutionMatch = lines[i].match(/RESOLUTION=(\d+x\d+)/); + const streamUrl = lines[i + 1]; + if (resolutionMatch && streamUrl && streamUrl.trim()) { + const resolution = resolutionMatch[1]; + const friendlyName = resolutionNames[resolution] || resolution; + streams.push({ + title: friendlyName, + streamUrl: streamUrl.trim(), + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + }); + } + } + } + + let englishSubtitleUrl = ""; + try { + const subResponse = await soraFetch(`https://mapple.uk/api/subtitles?id=${tmdbID}&mediaType=movie`); + const subData = await subResponse.json(); + const englishSub = subData.find(sub => sub.language === "en"); + if (englishSub) { + englishSubtitleUrl = englishSub.url; + } + } catch (e) { + englishSubtitleUrl = ""; + } + console.log("English Subtitle URL: " + englishSubtitleUrl); + console.log("Extracted streams: " + JSON.stringify(streams)); + + return JSON.stringify({ + streams: streams.length > 0 ? streams : [ + { + title: "Mapple Server", + streamUrl: streamData.data.stream_url, + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + } + ], + subtitle: englishSubtitleUrl || "" + }); + } else { + throw new Error("Failed to extract stream URL"); + } + } else if (ID.startsWith('/tv/') || ID.startsWith('tv/')) { + const match = ID.match(/tv\/(\d+)-(\d+)-(\d+)/); + if (!match) throw new Error("Invalid TV URL format"); + + const tmdbID = match[1]; + const seasonNumber = match[2]; + const episodeNumber = match[3]; + + const headers = { + "Content-Type": "text/plain", + "next-action": actionIdentifier + }; + const postData = `[{"mediaId":${tmdbID},"mediaType":"tv","tv_slug":"${seasonNumber}-${episodeNumber}","source":"mapple","useFallbackVideo":false,"sessionId":"${sessionID}"}]`; + const response = await fetchv2("https://mapple.uk/watch"+ ID, headers, "POST", postData); + const data = await response.text(); + console.log("Stream data: " + data); + + const lines = data.split('\n'); + let streamData = null; + for (const line of lines) { + if (line.includes('"success":true')) { + streamData = JSON.parse(line.substring(line.indexOf('{'))); + break; + } + } + + if (streamData && streamData.data && streamData.data.stream_url) { + const m3u8Response = await soraFetch(streamData.data.stream_url, { + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + }); + const m3u8Text = await m3u8Response.text(); + console.log("M3U8 Playlist: " + m3u8Text); + + const streams = []; + const lines = m3u8Text.split('\n'); + const resolutionNames = { + "3840x2160": "4K", + "1920x1080": "1080p", + "1280x720": "720p", + "640x360": "360p" + }; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith('#EXT-X-STREAM-INF')) { + const resolutionMatch = lines[i].match(/RESOLUTION=(\d+x\d+)/); + const streamUrl = lines[i + 1]; + if (resolutionMatch && streamUrl && streamUrl.trim()) { + const resolution = resolutionMatch[1]; + const friendlyName = resolutionNames[resolution] || resolution; + streams.push({ + title: friendlyName, + streamUrl: streamUrl.trim(), + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + }); + } + } + } + + let englishSubtitleUrl = ""; + try { + const subResponse = await soraFetch(`https://mapple.uk/api/subtitles?id=${tmdbID}&mediaType=tv&season=${seasonNumber}&episode=${episodeNumber}`); + const subData = await subResponse.json(); + const englishSub = subData.find(sub => sub.language === "en"); + if (englishSub) { + englishSubtitleUrl = englishSub.url; + } + } catch (e) { + englishSubtitleUrl = ""; + } + console.log("English Subtitle URL: " + englishSubtitleUrl); + console.log("Extracted streams: " + JSON.stringify(streams)); + + return JSON.stringify({ + streams: streams.length > 0 ? streams : [ + { + title: "Mapple Server", + streamUrl: streamData.data.stream_url, + headers: { + "referer": "https://mapple.uk/", + "origin": "https://mapple.uk" + } + } + ], + subtitle: englishSubtitleUrl || "" + }); + } else { + throw new Error("Failed to extract stream URL"); + } + } +} + + async function soraFetch(url, options = { headers: {}, method: 'GET', body: null, encoding: 'utf-8' }) { try { return await fetchv2( @@ -998,6 +1217,51 @@ async function ilovefeet(imdbId, isSeries = false, season = null, episode = null }; } +async function ilovethighs(watchUrl) { + const htmlResponse = await soraFetch(watchUrl); + const htmlText = await htmlResponse.text(); + + const layoutMatch = htmlText.match(/]*src="([^"]*app\/watch\/movie\/[^"]*layout-[^"]*\.js)"[^>]*><\/script>]*src="([^"]*\.js)"[^>]*><\/script>]*src="[^"]*app\/watch\/(movie|tv)\/[^"]*layout-[^"]*\.js"/); + + if (!beforeLayoutMatch || !beforeLayoutMatch[1]) { + throw new Error("error 2"); + } + + let targetUrl = beforeLayoutMatch[1]; + if (targetUrl.startsWith('/_next/')) { + targetUrl = 'https://mapple.uk' + targetUrl; + } else if (!targetUrl.startsWith('http')) { + targetUrl = 'https://mapple.uk/' + targetUrl; + } + + try { + const response = await soraFetch(targetUrl); + const text = await response.text(); + + let actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"[^"]*"getStreamUrl/); + if (!actionMatch) { + actionMatch = text.match(/createServerReference\)\("([a-f0-9]{40,})"/); + } + if (!actionMatch) { + actionMatch = text.match(/"([a-f0-9]{40,})"[^"]*"getStreamUrl/); + } + + if (actionMatch && actionMatch[1]) { + return actionMatch[1]; + } + } catch (e) { + throw new Error("error 3: " + e); + } + + throw new Error("error 4"); +} + function wordArrayToUint8Array(wordArray) { const words = wordArray.words; const sigBytes = wordArray.sigBytes; @@ -7226,6 +7490,4 @@ function uint8ArrayToBase64Url(bytes) { })); -//const CryptoJS = module.exports; - - +// Veni Vidi Veci \ No newline at end of file