# index.html -rw-r--r-- 555 bytes View raw
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
    <style>
        .hidden { display: none; }
    </style>
    <body>
        <div id="start">
            <p>
                <label for="video_url">URL video:</label>
                <input id="video_url">
            </p>
            <button id="start_button">Start</button>
        </div>


        <video id="player" class="hidden" controls></video>

        <div id="alert" class="hidden">Per avviare il video premi "play"</div>

        <a id="room_link" href="#"></a>

    </body>
    <script src="app.js"></script>
</html>
# app.js -rw-r--r-- 4.7 KiB View raw
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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);
}