let e_start_form = document.getElementById("start");
let e_video_url = document.getElementById("video_url");
let e_start_button = document.getElementById("start_button");
let e_player = document.getElementById("player");
let e_room_link = document.getElementById("room_link");
let e_alert = document.getElementById("alert");
let topic = ""; // topic su ntfy che useremo per inviare e ricevere i messaggi
let video_url = ""; // l'url del video che stiamo vedendo
let last_time = -1; // questo è l'ultima posizione nel video che abbiamo notificato
let player_state = "stop"; // stato del lettore, "stop" "play" "pause"
let im_control = false; // siamo noi i controllori nella stanza?
const MIN_T = 5; // tempo minimo tra le notifiche
let eventSource = null;
function on_message(evt) {
let data = JSON.parse(evt.data);
data = JSON.parse(data.message);
switch(data.type) {
case "sync":
// il messaggio è di tipo sync
// se sono il controllore, ho inviato io il messaggio,
// non ho bisogno di reagire.
if (im_control) return;
// se il lettore video ha un indirizzo diverso da quello
// nel messaggio, dobbiamo aggiornarlo
// Questo permette agli spettatori di caricare il video
// la prima volta che ricevono un messaggio di sync
if (data.url != e_player.currentSrc) e_player.src = data.url;
// il browser ci impedisce di passare da "stop" a "play" la prima volta
// usando javascript, quindi mostriamo il messaggio di avviso
if (player_state == "stop" && data.state == "play") {
e_alert.classList.remove("hidden");
return;
}
// se il mio lettore video è più di MIN_T lontano dalla posizione
// del messaggio allora mi riposiziono.
// questo lascia al player un po' di gioco per gestire ritardi di
// buffering senza essere sempre spostato mentre cerca di recuperare
// nuovi dati video.
if (Math.abs(data.currenttime - e_player.currentTime) > MIN_T) {
e_player.currentTime = data.currenttime;
}
// se lo stato del player del controllore è diverso da quello locale
// aggiorniamoci!
// se arriva lo stato stop, "scarico" il video dal lettore
// impostando un url vuoto
if (data.state != player_state) {
switch (data.state) {
case "pause":
e_player.pause();
break;
case "play":
e_player.play();
break;
case "stop":
e_player.src = "";
break;
}
}
break;
}
}
function send_message_sync() {
if (!im_control) return;
const ct = e_player.currentTime;
last_time = ct;
let data = {
'type':'sync',
'url': video_url,
'state': player_state,
'currenttime': ct,
}
fetch('https://ntfy.sh/' + topic, {
method: 'POST',
body: JSON.stringify(data),
});
}
function start(room_id) {
topic = room_id;
e_start_form.classList.add("hidden");
e_player.classList.remove("hidden");
e_room_link.href = "#" + topic;
e_room_link.innerText = topic;
eventSource = new EventSource('https://ntfy.sh/' + topic + '/sse');
eventSource.addEventListener('message', on_message);
if (video_url !== "") {
e_player.src = video_url;
}
}
function get_required_value(e) {
let v = e.value;
if (v == "") {
e.setCustomValidity("Field required.");
return null;
}
e.setCustomValidity("");
return v;
}
function create_room() {
video_url = get_required_value(e_video_url);
if (!video_url) return;
let room_id = Math.random().toString(16).substr(2, 20);
im_control = true;
start(room_id);
}
e_start_button.addEventListener("click", create_room);
e_player.addEventListener("timeupdate", (event) => {
if ( Math.abs(last_time - e_player.currentTime) > MIN_T ) send_message_sync();
});
e_player.addEventListener("play", (e) => {
player_state = "play";
send_message_sync();
e_alert.classList.add("hidden");
});
e_player.addEventListener("pause", (e) => {
player_state = "pause";
send_message_sync();
});
e_player.addEventListener("ended", (e) => {
player_state = "stop";
send_message_sync();
});
// avvia la stanza se c'è un valore nel fragment dell'url
let hash = window.location.hash.replace("#", "");
if (hash != "") {
start(hash);
}