anistream/client.js
ShakedAp bfdd897a20 Added settings file and removed videos from repo
Created settings.json that contains the settings
Created .gitignore that ignores the videos folder
Modified index.js to take the settings settings.json
Modified client.js to take the WebSocket settings from the window
2023-06-25 09:07:52 +03:00

173 lines
5.1 KiB
JavaScript

const vid = document.querySelector('video')
const socket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}`);
// if the new tms is within this margin of the current tms, then the change is ignored for smoother viewing
const PLAYING_THRESH = 1
const PAUSED_THRESH = 0.01
// local state
let video_playing = false
let last_updated = 0
let client_uid = null
// clocks sync variables
const num_time_sync_cycles = 10
let over_estimates = new Array()
let under_estimates = new Array()
let over_estimate = 0
let under_estimate = 0
let correction = 0
// Connection opened
socket.addEventListener('open', function (event) {
console.log('Connected to WS Server');
});
// Got message from the server
socket.addEventListener("message", (event) => {
if (event.data.startsWith("time_sync_response_backward"))
{
let time_at_server = Number(event.data.slice("time_sync_response_backward".length + 1));
let under_estimate_latest = time_at_server - get_global_time(0);
under_estimates.push(under_estimate_latest);
under_estimate = median(under_estimates);
correction = (under_estimate + over_estimate)/2;
console.log(`%c Updated val for under_estimate is ${under_estimate}`, "color:green");
console.log(`%c New correction time is ${correction} miliseconds`, 'color:red; font-size:12px');
}
if (event.data.startsWith("time_sync_response_forward"))
{
let calculated_diff = Number(event.data.slice("time_sync_response_forward".length + 1));
over_estimates.push(calculated_diff);
over_estimate = median(over_estimates);
correction = (under_estimate + over_estimate)/2;
console.log(`%c Updated val for over_estimate is ${over_estimate}`, "color:green");
console.log(`%c New correction time is ${correction} miliseconds`, 'color:red; font-size:12px');
}
if (event.data.startsWith("state_update_from_server"))
{
let state = JSON.parse(event.data.slice("state_update_from_server".length + 1));
// Whenever the client connects or reconnects
if (client_uid == null){
client_uid = state.client_uid;
}
// calculating the new timestamp for both cases - when the video is playing and when it is paused
let proposed_time = (state.playing) ? ((get_global_time(correction) - state.global_timestamp)/1000 + state.video_timestamp) : (state.video_timestamp)
let gap = Math.abs(proposed_time - vid.currentTime)
console.log(`%cGap was ${proposed_time - vid.currentTime}`, 'font-size:12px; color:purple')
if (state.playing){
if(gap > PLAYING_THRESH){
// tolerance while the video is playing
vid.currentTime = proposed_time
}
vid.play()
}
else{
vid.pause()
if (gap > PAUSED_THRESH){
// condition to prevent an unnecessary seek
vid.currentTime = proposed_time
}
}
}
});
// Connection closed
socket.addEventListener('close', function (event) {
console.log('Disconnected from the WS Server');
client_uid = null
});
function state_change_handler(event)
{
if (event !== null && event !== undefined){
if (event.type === 'pause')
video_playing = false;
else if (event.type === 'play')
video_playing = true;
}
last_updated = get_global_time(correction);
state_image = {
video_timestamp: vid.currentTime,
last_updated: last_updated,
playing: video_playing,
global_timestamp: get_global_time(correction),
client_uid: client_uid
}
socket.send(`state_update_from_client ${JSON.stringify(state_image)}`)
}
// assigning event handlers
vid.onseeking = state_change_handler;
vid.onplay = state_change_handler;
vid.onpause = state_change_handler;
// handling the video ended case separately
vid.onended = () => {
video_playing = false;
last_updated = get_global_time(correction);
vid.load();
state_change_handler();
}
// Helper Functions
function get_global_time(delta = 0) {
let d = new Date();
return d.getTime() + delta;
}
async function get_settings() {
let s = null;
await fetch('settings.json')
.then((response)=>response.json())
.then((responseJson)=>{s = responseJson});
return s;
}
function median(values) {
if(values.length === 0) { return 0; }
values.sort((x,y) => (x-y));
let half = Math.floor(values.length / 2);
if (values.length % 2){
return values[half];
}
return (values[half - 1] + values[half]) / 2.0;
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function do_time_sync_one_cycle_backward() {
socket.send("time_sync_request_backward");
}
function do_time_sync_one_cycle_forward() {
socket.send(`time_sync_request_forward ${get_global_time()}`);
}
// time requests are made every second
async function do_time_sync() {
for(let i = 0; i < num_time_sync_cycles; i++){
await timeout(500);
do_time_sync_one_cycle_backward();
await timeout(500);
do_time_sync_one_cycle_forward();
}
}
do_time_sync();