diff --git a/faselhd/faselhd.js b/faselhd/faselhd.js index 0ac1f04..8769972 100644 --- a/faselhd/faselhd.js +++ b/faselhd/faselhd.js @@ -132,29 +132,101 @@ async function extractStreamUrl(url) { const regex = /]*>\s*(\(function\([\s\S]*?)\s*<\/script>/i; + const scriptMatch = html2.match(scriptRegex); + if (scriptMatch && scriptMatch[1]) { + const postData = { + code: scriptMatch[1].trim(), + options: { + jsx: true, + unpack: true, + unminify: true, + deobfuscate: true, + mangle: false + } + }; + const decryptResp = await fetchv2("https://webcrack-fdpm.vercel.app/api/decrypt", { "Content-Type": "application/json" }, "POST", postData); + const result = await decryptResp.json(); + const deobfuscatedCode = result.code; + + const extracted = (function(code) { + let m = code.match(/sources:\s*\[\{\s*file:\s*"([^"]+\.m3u8)"/); + if (m) return m[1]; + m = code.match(/data-url="([^"]+\.m3u8)"/); + if (m) return m[1]; + m = code.match(/file:\s*"([^"]+\.m3u8)"/); + if (m) return m[1]; + return null; + })(deobfuscatedCode); + + if (extracted) { + const final = await a(extracted); + return final || ""; + } + } + + try { + const netRes = await networkFetch(primaryUrl, { timeoutSeconds: 2, returnHTML: true }); + const netHtml = netRes.html || ""; + + const fm = netHtml.match(/data-url="([^"]+\.m3u8)"/) + || netHtml.match(/sources:\s*\[\{\s*file:\s*"([^"]+\.m3u8)"/) + || netHtml.match(/file:\s*"([^"]+\.m3u8)"/); + + const found = fm ? fm[1] : ""; + const final = found ? await a(found) : ""; + return final || ""; + } catch { return ""; } - const streamUrl = match[1].trim(); - - console.log(streamUrl); - - const response2 = await networkFetch(streamUrl, { - timeoutSeconds: 2, - returnHTML: true - }); - const html2 = response2.html; - - const match2 = html2.match(/data-url="([^"]+\.m3u8)"/); - if (match2) { - return match2[1]; - } else { - return null; - } - } catch (err) { - console.log("Error fetching stream URL content:"+ err); + } catch { return ""; } } + +async function a(streamUrl) { + try { + const looksLikeMaster = /master\.m3u8|\/stream\/v2\//i.test(streamUrl); + if (!looksLikeMaster) return streamUrl; + + const res = await fetchv2(streamUrl); + const txt = await res.text(); + + const lines = txt.split(/\r?\n/); + const variants = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (line.startsWith('#EXT-X-STREAM-INF:')) { + const info = line; + const urlLine = (lines[i+1] || "").trim(); + if (!urlLine || urlLine.startsWith('#')) continue; + + const resMatch = info.match(/RESOLUTION=(\d+)x(\d+)/i); + const bwMatch = info.match(/BANDWIDTH=(\d+)/i); + const labelMatch = info.match(/NAME="([^"]+)"/i); + + const width = resMatch ? parseInt(resMatch[1], 10) : 0; + const height = resMatch ? parseInt(resMatch[2], 10) : 0; + const bw = bwMatch ? parseInt(bwMatch[1], 10) : 0; + + variants.push({ url: urlLine, width, height, bw, label: labelMatch ? labelMatch[1] : "" }); + } + } + + if (variants.length === 0) return streamUrl; + + const prefer1080 = variants.find(v => v.height >= 1000 || /1080/i.test(v.url) || /1080/i.test(v.label)); + if (prefer1080) return prefer1080.url; + + variants.sort((a,b) => b.bw - a.bw); + return variants[0].url || streamUrl; + } catch { + return streamUrl; + } +}