mirror of
git://f0xx.org/ac/ac-be-issues
synced 2026-07-29 06:38:32 +03:00
211 lines
6.5 KiB
JavaScript
211 lines
6.5 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
var MAX_DEMO_S = 300;
|
|
var timer = null;
|
|
var secondsLeft = MAX_DEMO_S;
|
|
var mediaStream = null;
|
|
var sessionId = '';
|
|
var webrtc = null;
|
|
|
|
function basePath() {
|
|
return document.body.getAttribute('data-base-path') || '';
|
|
}
|
|
|
|
function el(id) {
|
|
return document.getElementById(id);
|
|
}
|
|
|
|
function detectPlatform() {
|
|
var ua = navigator.userAgent || '';
|
|
if (/Firefox\//i.test(ua)) return 'firefox';
|
|
if (/Edg\//i.test(ua)) return 'edge';
|
|
if (/Chrome\//i.test(ua)) return 'chrome';
|
|
if (/Safari\//i.test(ua)) return 'safari';
|
|
return 'desktop';
|
|
}
|
|
|
|
function apiUrl() {
|
|
return basePath() + '/api/live_cast.php';
|
|
}
|
|
|
|
function postJson(body, cb) {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('POST', apiUrl(), true);
|
|
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
|
|
xhr.onload = function () {
|
|
var payload = null;
|
|
try {
|
|
payload = JSON.parse(xhr.responseText);
|
|
} catch (e) {
|
|
cb(null);
|
|
return;
|
|
}
|
|
cb(payload);
|
|
};
|
|
xhr.onerror = function () { cb(null); };
|
|
xhr.send(JSON.stringify(body));
|
|
}
|
|
|
|
function setStatus(text) {
|
|
var node = el('edu-status');
|
|
if (node) node.textContent = text;
|
|
}
|
|
|
|
function updateTimer() {
|
|
var node = el('edu-timer');
|
|
if (!node) return;
|
|
var m = Math.floor(secondsLeft / 60);
|
|
var s = secondsLeft % 60;
|
|
node.textContent = m + ':' + (s < 10 ? '0' : '') + s;
|
|
}
|
|
|
|
function renderShare(session) {
|
|
var box = el('edu-share');
|
|
if (!box || !session) return;
|
|
var joinUrl = session.join_short_url || '';
|
|
var qrUrl = session.join_qr_url || '';
|
|
var longJoin = basePath() + '/live/join?session_id=' + encodeURIComponent(session.session_id || sessionId);
|
|
if (!joinUrl) joinUrl = longJoin;
|
|
var html = '<h2>Share this demo</h2><p class="muted">Viewers open this link in another browser (same LAN or online).</p>';
|
|
html += '<p><a href="' + joinUrl + '" target="_blank" rel="noopener">' + joinUrl + '</a></p>';
|
|
if (qrUrl) {
|
|
html += '<figure class="twofa-qr-wrap"><img src="' + qrUrl + '" width="160" height="160" alt="Viewer QR code"><figcaption class="muted">Scan to open viewer page</figcaption></figure>';
|
|
}
|
|
box.hidden = false;
|
|
box.innerHTML = html;
|
|
}
|
|
|
|
function stopDemo() {
|
|
if (timer) {
|
|
clearInterval(timer);
|
|
timer = null;
|
|
}
|
|
if (webrtc) {
|
|
webrtc.stop();
|
|
webrtc = null;
|
|
}
|
|
if (mediaStream) {
|
|
mediaStream.getTracks().forEach(function (t) { t.stop(); });
|
|
mediaStream = null;
|
|
}
|
|
var preview = el('edu-preview');
|
|
if (preview) preview.srcObject = null;
|
|
if (sessionId) {
|
|
postJson({ action: 'heartbeat', session_id: sessionId, status: 'ended' }, function () {});
|
|
sessionId = '';
|
|
}
|
|
var share = el('edu-share');
|
|
if (share) share.hidden = true;
|
|
el('edu-start').disabled = false;
|
|
el('edu-stop').disabled = true;
|
|
setStatus('Demo ended. Thanks for trying AndroidCast education mode.');
|
|
}
|
|
|
|
function startWebRtc() {
|
|
if (!window.LiveCastWebRtc || !sessionId || !mediaStream) return;
|
|
webrtc = window.LiveCastWebRtc.connect({
|
|
basePath: basePath(),
|
|
sessionId: sessionId,
|
|
role: 'caster',
|
|
localStream: mediaStream,
|
|
onStatus: setStatus,
|
|
onStats: function (stats) {
|
|
var nerds = el('edu-stats');
|
|
if (!nerds) return;
|
|
nerds.hidden = false;
|
|
nerds.textContent = 'Stats: ICE ' + (stats.ice_state || '—')
|
|
+ ' · out ' + (stats.outbound_kbps || 0) + ' kbps'
|
|
+ ' · RTT ' + (stats.rtt_ms || 0) + ' ms';
|
|
}
|
|
});
|
|
}
|
|
|
|
function startDemo() {
|
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
|
|
setStatus('Screen sharing is not supported in this browser.');
|
|
return;
|
|
}
|
|
if (!window.RTCPeerConnection) {
|
|
setStatus('WebRTC is not available in this browser.');
|
|
return;
|
|
}
|
|
setStatus('Requesting screen share permission…');
|
|
navigator.mediaDevices.getDisplayMedia({ video: true, audio: false })
|
|
.then(function (stream) {
|
|
mediaStream = stream;
|
|
var preview = el('edu-preview');
|
|
if (preview) {
|
|
preview.hidden = false;
|
|
preview.srcObject = stream;
|
|
preview.play().catch(function () {});
|
|
}
|
|
stream.getVideoTracks()[0].addEventListener('ended', stopDemo);
|
|
setStatus('Creating demo session…');
|
|
postJson({
|
|
action: 'create_intent',
|
|
platform: 'education_' + detectPlatform(),
|
|
max_duration_s: MAX_DEMO_S,
|
|
codec_video: 'browser_screen',
|
|
codec_audio: 'none',
|
|
bw_mode: 'demo'
|
|
}, function (resp) {
|
|
if (!resp || !resp.ok || !resp.session) {
|
|
setStatus('Could not register demo session. Try again later.');
|
|
stopDemo();
|
|
return;
|
|
}
|
|
sessionId = resp.session.session_id || '';
|
|
renderShare(resp.session);
|
|
startWebRtc();
|
|
secondsLeft = MAX_DEMO_S;
|
|
updateTimer();
|
|
el('edu-start').disabled = true;
|
|
el('edu-stop').disabled = false;
|
|
setStatus('Live demo — share the link so others can watch.');
|
|
timer = setInterval(function () {
|
|
secondsLeft -= 1;
|
|
updateTimer();
|
|
if (secondsLeft <= 0) {
|
|
stopDemo();
|
|
}
|
|
}, 1000);
|
|
if (window.AndroidCastAnalytics && window.AndroidCastAnalytics.trackEvent) {
|
|
window.AndroidCastAnalytics.trackEvent('live_education_start', { platform: detectPlatform() });
|
|
}
|
|
});
|
|
})
|
|
.catch(function () {
|
|
setStatus('Screen share permission denied or cancelled.');
|
|
});
|
|
}
|
|
|
|
function boot() {
|
|
if (!el('live-education-app')) return;
|
|
updateTimer();
|
|
var startBtn = el('edu-start');
|
|
var stopBtn = el('edu-stop');
|
|
if (startBtn) startBtn.addEventListener('click', startDemo);
|
|
if (stopBtn) {
|
|
stopBtn.disabled = true;
|
|
stopBtn.addEventListener('click', stopDemo);
|
|
}
|
|
var notifBtn = el('edu-notify');
|
|
if (notifBtn && 'Notification' in window) {
|
|
notifBtn.addEventListener('click', function () {
|
|
Notification.requestPermission().then(function (perm) {
|
|
setStatus(perm === 'granted' ? 'Notifications enabled.' : 'Notifications not granted.');
|
|
});
|
|
});
|
|
} else if (notifBtn) {
|
|
notifBtn.hidden = true;
|
|
}
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', boot);
|
|
} else {
|
|
boot();
|
|
}
|
|
})();
|