102 lines
4.9 KiB
JavaScript
102 lines
4.9 KiB
JavaScript
'use strict';
|
|
(function () {
|
|
const $ = id => document.getElementById(id);
|
|
const roomId = decodeURIComponent((location.pathname.match(/^\/room\/([A-Za-z0-9_-]+)\/?$/) || [])[1] || '');
|
|
let adminSecret = new URLSearchParams(location.hash.slice(1)).get('key') || '';
|
|
let adminLink = '';
|
|
let audienceUrl = roomId ? location.origin + '/watch/' + roomId : '';
|
|
|
|
function show(id) {
|
|
for (const section of ['loading', 'denied', 'room']) $(section).hidden = section !== id;
|
|
}
|
|
function setStatus(id, state, text) {
|
|
$(id + 'Status').textContent = text;
|
|
window.CaptionStatus?.set(id + 'Dot', state, text);
|
|
}
|
|
function fail(message) {
|
|
setStatus('room', 'danger', message);
|
|
$('deniedText').textContent = message; show('denied');
|
|
}
|
|
async function checkService() {
|
|
try {
|
|
const response = await fetch('/health', {credentials: 'same-origin', signal: AbortSignal.timeout(10000)});
|
|
if (!response.ok) throw new Error();
|
|
const info = await response.json();
|
|
setStatus('service', info.ready ? 'good' : 'warn', info.ready ? 'Speech service ready' : 'Speech service starting');
|
|
} catch (_) {
|
|
setStatus('service', 'danger', 'Speech service unavailable');
|
|
}
|
|
}
|
|
async function api(path, options = {}) {
|
|
const headers = new Headers(options.headers || {});
|
|
if (options.body) headers.set('Content-Type', 'application/json');
|
|
return fetch(path, {...options, headers, credentials: 'same-origin', signal: AbortSignal.timeout(10000)});
|
|
}
|
|
function setAdminLink(value) {
|
|
adminLink = value;
|
|
$('adminLink').textContent = value || 'Not available in this tab — create a fresh link below.';
|
|
$('copyAdmin').disabled = !value;
|
|
}
|
|
async function copy(value, button, label) {
|
|
try {
|
|
await navigator.clipboard.writeText(value);
|
|
button.textContent = 'Copied';
|
|
setTimeout(() => { button.textContent = label; }, 1500);
|
|
} catch (_) {
|
|
$('result').textContent = 'Select the link above and copy it.';
|
|
}
|
|
}
|
|
function renderRoom(room, canonicalAudience) {
|
|
const name = (room.label || '').trim() || 'Caption Admin Room';
|
|
$('roomName').textContent = name;
|
|
document.title = name === 'Caption Admin Room' ? name : name + ' — Caption Admin Room';
|
|
$('roomId').textContent = room.id;
|
|
$('created').textContent = room.created ? new Date(room.created * 1000).toLocaleString() : '';
|
|
if (canonicalAudience) audienceUrl = canonicalAudience;
|
|
$('captureLink').href = '/capture-local.html';
|
|
$('editorLink').href = '/room/' + encodeURIComponent(room.id) + '/edit';
|
|
$('audienceLink').href = audienceUrl;
|
|
$('audienceLinkText').textContent = audienceUrl;
|
|
window.CaptionRoomMenu?.set({roomId: room.id, audienceUrl});
|
|
setAdminLink(adminLink);
|
|
setStatus('room', 'good', 'Room active');
|
|
show('room');
|
|
}
|
|
async function boot() {
|
|
if (!roomId) { fail('This room address is invalid.'); return; }
|
|
if (adminSecret) {
|
|
const response = await api('/api/rooms/' + encodeURIComponent(roomId) + '/session', {
|
|
method: 'POST', body: JSON.stringify({key: adminSecret})
|
|
});
|
|
if (!response.ok) { fail('This admin link is invalid or the room has been closed.'); return; }
|
|
adminLink = location.origin + '/room/' + roomId + '#key=' + adminSecret;
|
|
history.replaceState(null, '', '/room/' + roomId);
|
|
}
|
|
const response = await api('/api/rooms/' + encodeURIComponent(roomId));
|
|
if (response.status === 401) { fail('Open this room with its private admin link.'); return; }
|
|
if (!response.ok) { fail('This room is closed or unavailable.'); return; }
|
|
const data = await response.json();
|
|
renderRoom(data.room, data.audienceUrl);
|
|
}
|
|
$('copyAdmin').onclick = () => copy(adminLink, $('copyAdmin'), 'Copy admin link');
|
|
$('copyAudience').onclick = () => copy(audienceUrl, $('copyAudience'), 'Copy audience link');
|
|
$('rotate').onclick = async () => {
|
|
if (!confirm('Replace the admin link? The old admin link and existing editor sessions will stop working. Audience links keep working.')) return;
|
|
const response = await api('/api/rooms/' + encodeURIComponent(roomId) + '/rotate', {method: 'POST'});
|
|
const data = await response.json().catch(() => ({}));
|
|
if (!response.ok) { $('result').textContent = data.detail || 'Could not create a new link.'; return; }
|
|
adminSecret = data.adminUrl.split('#key=')[1] || '';
|
|
setAdminLink(adminSecret ? location.origin + '/room/' + roomId + '#key=' + adminSecret : '');
|
|
$('result').textContent = 'Fresh admin link created. Copy it now and keep it private.';
|
|
};
|
|
$('close').onclick = async () => {
|
|
if (!confirm('Close this admin room? Admin, editor and audience links will stop working.')) return;
|
|
const response = await api('/api/rooms/' + encodeURIComponent(roomId), {method: 'DELETE'});
|
|
if (!response.ok) { $('result').textContent = 'This admin room could not be closed.'; return; }
|
|
setStatus('room', 'danger', 'Room closed');
|
|
fail('This admin room has been closed.');
|
|
};
|
|
checkService(); setInterval(checkService, 30000);
|
|
boot().catch(() => fail('The service could not be reached. Try again.'));
|
|
})();
|