restreamer-ui-v2/public/oauth2callback.html

203 lines
6.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>YouTube OAuth2 Callback</title>
<style>
body {
background: #1a1a1a;
color: #fff;
font-family: sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
flex-direction: column;
}
.card {
background: #2a2a2a;
border-radius: 8px;
padding: 32px 40px;
text-align: center;
max-width: 460px;
width: 90%;
}
.icon { font-size: 3rem; margin-bottom: 12px; }
h2 { margin: 0 0 8px; font-size: 1.2rem; }
p { color: #aaa; font-size: 0.9rem; margin: 0 0 8px; }
.error { color: #f44336; }
.success { color: #4caf50; }
.warn { color: #ff9800; }
pre {
background: #111;
padding: 12px;
border-radius: 4px;
word-break: break-all;
font-size: 0.75rem;
margin-top: 12px;
color: #4fc3f7;
text-align: left;
max-height: 120px;
overflow-y: auto;
}
button {
margin-top: 12px;
padding: 8px 20px;
background: #1976d2;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
}
button:hover { background: #1565c0; }
.progress {
width: 100%;
height: 4px;
background: #333;
border-radius: 2px;
margin-top: 16px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: #4caf50;
animation: progress 1.5s linear forwards;
}
@keyframes progress { from { width: 0%; } to { width: 100%; } }
</style>
</head>
<body>
<div class="card" id="container">
<div class="icon" id="icon"></div>
<h2 id="title">Processing authorization...</h2>
<p id="message">Please wait, do not close this window.</p>
</div>
<script>
(function () {
var params = new URLSearchParams(window.location.search);
var code = params.get('code');
var error = params.get('error');
var state = params.get('state');
var icon = document.getElementById('icon');
var title = document.getElementById('title');
var message = document.getElementById('message');
var container = document.getElementById('container');
function setStatus(ic, ti, msg, cls) {
icon.textContent = ic;
title.textContent = ti;
message.textContent = msg;
if (cls) message.className = cls;
}
/* ── ERROR ───────────────────────────────────────────────── */
if (error) {
setStatus('❌', 'Authorization denied', error, 'error');
if (window.opener) {
window.opener.postMessage({ service: 'youtube', error: error }, '*');
}
setTimeout(function () { window.close(); }, 3000);
return;
}
/* ── CODE (authorization code flow) ─────────────────────── */
if (code) {
setStatus('✅', 'Authorization successful!', 'Sending to Restreamer…', 'success');
// Barra de progreso visual
var prog = document.createElement('div');
prog.className = 'progress';
var bar = document.createElement('div');
bar.className = 'progress-bar';
prog.appendChild(bar);
container.appendChild(prog);
if (window.opener) {
// Enviar con reintentos hasta que el padre responda "ack"
var ackReceived = false;
var attempts = 0;
var maxAttempts = 10;
// Escuchar el ACK del padre
window.addEventListener('message', function onAck(ev) {
if (ev.data && ev.data.service === 'youtube' && ev.data.ack === true) {
ackReceived = true;
window.removeEventListener('message', onAck);
setStatus('✅', 'Done!', 'This window will close automatically.', 'success');
setTimeout(function () { window.close(); }, 800);
}
});
function sendCode() {
if (ackReceived || attempts >= maxAttempts) return;
attempts++;
try {
window.opener.postMessage(
{ service: 'youtube', code: code, state: state },
window.location.origin // mismo origen
);
} catch (e) {
// Si falla por política de origen, intentar con '*'
try { window.opener.postMessage({ service: 'youtube', code: code, state: state }, '*'); } catch (e2) {}
}
setTimeout(sendCode, 600);
}
sendCode();
// Si tras 8 s no hubo ack, mostrar código manual
setTimeout(function () {
if (!ackReceived) {
setStatus('⚠️', 'Could not reach Restreamer', 'Copy this code and paste it manually:', 'warn');
var pre = document.createElement('pre');
pre.textContent = code;
container.appendChild(pre);
var btn = document.createElement('button');
btn.textContent = '📋 Copy code';
btn.onclick = function () {
navigator.clipboard.writeText(code).then(function () { btn.textContent = '✅ Copied!'; });
};
container.appendChild(btn);
}
}, 8000);
} else {
// Abierto directamente (sin window.opener)
setStatus('⚠️', 'No parent window found', 'Copy this authorization code:', 'warn');
var pre = document.createElement('pre');
pre.textContent = code;
container.appendChild(pre);
var btn = document.createElement('button');
btn.textContent = '📋 Copy code';
btn.onclick = function () {
navigator.clipboard.writeText(code).then(function () { btn.textContent = '✅ Copied!'; });
};
container.appendChild(btn);
}
return;
}
/* ── TOKEN IMPLÍCITO (hash) ──────────────────────────────── */
var hashParams = new URLSearchParams(window.location.hash.replace('#', '?'));
var token = hashParams.get('access_token');
if (token) {
setStatus('✅', 'Token received!', 'Sending to Restreamer…', 'success');
if (window.opener) {
window.opener.postMessage({ service: 'youtube', access_token: token }, '*');
setTimeout(function () { window.close(); }, 1500);
}
return;
}
/* ── NADA ÚTIL ───────────────────────────────────────────── */
setStatus('⚠️', 'No authorization data found', 'This page should only be opened by the OAuth2 flow.', 'warn');
})();
</script>
</body>
</html>