203 lines
6.5 KiB
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>
|
|
|