function decodeHtmlEntities(text) { return text .replace(/’/g, "'") .replace(/“/g, '"') .replace(/”/g, '"') .replace(/…/g, '...') .replace(/&/g, '&') .replace(/"/g, '"') .replace(/</g, '<') .replace(/>/g, '>') .replace(/&#(\d+);/g, (_, num) => String.fromCharCode(num)); } async function searchResults(keyword) { const results = []; try { const response = await fetchv2("https://www.faselhds.xyz/?s=" + encodeURIComponent(keyword)); const html = await response.text(); const regex = /
[\s\S]*?[\s\S]*?]+src="([^"]+)"[\s\S]*?
([\s\S]*?)<\/div>/g; let match; while ((match = regex.exec(html)) !== null) { results.push({ title: match[3].trim(), image: match[2].trim(), href: match[1].trim() }); } return JSON.stringify(results); } catch (err) { return JSON.stringify([{ title: "Error", image: "Error", href: "Error" }]); } } async function extractDetails(url) { try { const response = await fetchv2(url); const html = await response.text(); const match = /
\s*

([\s\S]*?)<\/p>/.exec(html); const description = match ? match[1].trim() : "N/A"; return JSON.stringify([{ description: decodeHtmlEntities(description), aliases: "N/A", airdate: "N/A" }]); } catch (err) { return JSON.stringify([{ description: "Error", aliases: "Error", airdate: "Error" }]); } } async function extractEpisodes(url) { const baseUrl = "https://www.faselhds.xyz"; const allEpisodes = []; function extractEpisodesFromHtml(html) { const episodes = []; const regex = /]*>\s*الحلقة\s*(\d+)\s*<\/a>/g; let match; while ((match = regex.exec(html)) !== null) { episodes.push({ href: match[1].trim(), number: parseInt(match[2], 10), }); } return episodes; } try { const response = await fetchv2(url); const html = await response.text(); const seasonDivRegex = /]+class=["'][^"']*seasonDiv[^"']*["'][^>]*>/g; const seasonMatches = html.match(seasonDivRegex); const seasonCount = seasonMatches ? seasonMatches.length : 0; console.log(`Found ${seasonCount} seasons`); if (seasonCount > 1) { const seasonHrefRegex = /]+class=["'][^"']*seasonDiv[^"']*["'][^>]*onclick=["']window\.location\.href\s*=\s*['"](([^'"]+))['"][^>]*>/g; const seasonPaths = []; let match; while ((match = seasonHrefRegex.exec(html)) !== null) { seasonPaths.push(match[1]); } for (const path of seasonPaths) { const seasonUrl = path.startsWith("http") ? path : baseUrl + path; const seasonResponse = await fetchv2(seasonUrl); const seasonPageHtml = await seasonResponse.text(); const episodes = extractEpisodesFromHtml(seasonPageHtml); allEpisodes.push(...episodes); } return JSON.stringify(allEpisodes); } else { const episodes = extractEpisodesFromHtml(html); if (episodes.length === 0) { return JSON.stringify([{ href: url, number: 1 }]); } return JSON.stringify(episodes); } } catch (err) { console.error("Error:", err); return JSON.stringify([{ href: "Error", number: "Error" }]); } } async function extractStreamUrl(url) { try { const response = await fetchv2(url); const html = await response.text(); 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 ""; } } 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; } }