|
<!DOCTYPE html> |
|
<html lang="id"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
|
<title>GPT-5</title> |
|
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> |
|
<meta http-equiv="Pragma" content="no-cache"> |
|
<meta http-equiv="Expires" content="0"> |
|
|
|
<style> |
|
html, body { margin: 0; padding: 0; width: 100%; height: 100%; } |
|
#loading { |
|
position: fixed; inset: 0; background: rgba(255,255,255,0.9); |
|
display: flex; justify-content: center; align-items: center; |
|
font-size: 18px; z-index: 1000; |
|
} |
|
iframe { width: 100%; height: 100vh; border: none; } |
|
</style> |
|
</head> |
|
<body> |
|
<div id="loading">Please wait... Memilih server optimal...</div> |
|
<iframe id="streamlit-frame" referrerpolicy="no-referrer"></iframe> |
|
|
|
<script> |
|
const VERSION = "v1"; |
|
const SERVERS = [ |
|
"https://gepatest-hwak3xkal5nltlveztf5nn.streamlit.app/?embed=true" |
|
]; |
|
|
|
|
|
const pingTimeoutMs = 5000; |
|
const iframeLoadTimeoutMs = 12000; |
|
const overallTryTimeoutMs = 20000; |
|
|
|
|
|
function ping(url, timeout = pingTimeoutMs) { |
|
return new Promise((resolve) => { |
|
const start = performance.now(); |
|
const img = new Image(); |
|
let done = false; |
|
|
|
const timer = setTimeout(() => { |
|
if (done) return; |
|
done = true; |
|
|
|
resolve({ url, ok: false, latency: Infinity }); |
|
img.src = ""; |
|
}, timeout); |
|
|
|
const onComplete = (ok) => { |
|
if (done) return; |
|
done = true; |
|
clearTimeout(timer); |
|
const latency = performance.now() - start; |
|
resolve({ url, ok, latency }); |
|
}; |
|
|
|
img.onload = () => onComplete(true); |
|
img.onerror = () => onComplete(false); |
|
|
|
|
|
|
|
|
|
const u = new URL(url); |
|
u.searchParams.set("cb", Date.now().toString()); |
|
img.src = u.toString(); |
|
}); |
|
} |
|
|
|
async function rankServersByPing(urls) { |
|
const results = await Promise.all(urls.map(u => ping(u))); |
|
|
|
return results |
|
.sort((a,b) => a.latency - b.latency) |
|
.map(r => r.url); |
|
} |
|
|
|
function setIframeSrc(iframe, baseUrl) { |
|
const u = new URL(baseUrl); |
|
u.searchParams.set("cb", Date.now().toString()); |
|
iframe.src = u.toString(); |
|
} |
|
|
|
|
|
function tryLoadServer(iframe, url) { |
|
return new Promise((resolve) => { |
|
let settled = false; |
|
|
|
|
|
const hardTimer = setTimeout(() => { |
|
if (settled) return; |
|
settled = true; |
|
resolve(false); |
|
}, overallTryTimeoutMs); |
|
|
|
|
|
const readyTimer = setTimeout(() => { |
|
if (settled) return; |
|
|
|
settled = true; |
|
clearTimeout(hardTimer); |
|
resolve(false); |
|
}, iframeLoadTimeoutMs); |
|
|
|
|
|
iframe.onload = () => { |
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
if (settled) return; |
|
settled = true; |
|
clearTimeout(readyTimer); |
|
clearTimeout(hardTimer); |
|
resolve(true); |
|
}, 1500); |
|
}; |
|
|
|
|
|
iframe.onerror = () => { |
|
if (settled) return; |
|
settled = true; |
|
clearTimeout(readyTimer); |
|
clearTimeout(hardTimer); |
|
resolve(false); |
|
}; |
|
|
|
setIframeSrc(iframe, url); |
|
}); |
|
} |
|
|
|
async function loadWithFallback() { |
|
const iframe = document.getElementById("streamlit-frame"); |
|
const loading = document.getElementById("loading"); |
|
const storageKey = `streamlitUrl_${VERSION}`; |
|
|
|
|
|
const cached = sessionStorage.getItem(storageKey); |
|
|
|
|
|
let prioritized = await rankServersByPing(SERVERS); |
|
|
|
|
|
if (cached && prioritized.includes(cached)) { |
|
prioritized = [cached, ...prioritized.filter(u => u !== cached)]; |
|
} |
|
|
|
for (const url of prioritized) { |
|
const ok = await tryLoadServer(iframe, url); |
|
if (ok) { |
|
|
|
sessionStorage.setItem(storageKey, url); |
|
loading.style.display = "none"; |
|
|
|
iframe.addEventListener('error', () => { |
|
sessionStorage.removeItem(storageKey); |
|
}); |
|
return; |
|
} |
|
} |
|
|
|
|
|
sessionStorage.removeItem(storageKey); |
|
loading.textContent = "Semua server gagal dimuat. Coba refresh halaman."; |
|
} |
|
|
|
window.addEventListener("load", loadWithFallback); |
|
</script> |
|
</body> |
|
</html> |