WebHost: update trackers only if they're visible. (#3407)
This commit is contained in:
parent
13bc121c27
commit
da33d1576a
WebHostLib
|
@ -27,7 +27,7 @@ const adjustTableHeight = () => {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
const secondsToHours = (seconds) => {
|
const secondsToHours = (seconds) => {
|
||||||
let hours = Math.floor(seconds / 3600);
|
let hours = Math.floor(seconds / 3600);
|
||||||
let minutes = Math.floor((seconds - (hours * 3600)) / 60).toString().padStart(2, '0');
|
let minutes = Math.floor((seconds - (hours * 3600)) / 60).toString().padStart(2, '0');
|
||||||
return `${hours}:${minutes}`;
|
return `${hours}:${minutes}`;
|
||||||
};
|
};
|
||||||
|
@ -38,18 +38,18 @@ window.addEventListener('load', () => {
|
||||||
info: false,
|
info: false,
|
||||||
dom: "t",
|
dom: "t",
|
||||||
stateSave: true,
|
stateSave: true,
|
||||||
stateSaveCallback: function(settings, data) {
|
stateSaveCallback: function (settings, data) {
|
||||||
delete data.search;
|
delete data.search;
|
||||||
localStorage.setItem(`DataTables_${settings.sInstance}_/tracker`, JSON.stringify(data));
|
localStorage.setItem(`DataTables_${settings.sInstance}_/tracker`, JSON.stringify(data));
|
||||||
},
|
},
|
||||||
stateLoadCallback: function(settings) {
|
stateLoadCallback: function (settings) {
|
||||||
return JSON.parse(localStorage.getItem(`DataTables_${settings.sInstance}_/tracker`));
|
return JSON.parse(localStorage.getItem(`DataTables_${settings.sInstance}_/tracker`));
|
||||||
},
|
},
|
||||||
footerCallback: function(tfoot, data, start, end, display) {
|
footerCallback: function (tfoot, data, start, end, display) {
|
||||||
if (tfoot) {
|
if (tfoot) {
|
||||||
const activityData = this.api().column('lastActivity:name').data().toArray().filter(x => !isNaN(x));
|
const activityData = this.api().column('lastActivity:name').data().toArray().filter(x => !isNaN(x));
|
||||||
Array.from(tfoot?.children).find(td => td.classList.contains('last-activity')).innerText =
|
Array.from(tfoot?.children).find(td => td.classList.contains('last-activity')).innerText =
|
||||||
(activityData.length) ? secondsToHours(Math.min(...activityData)) : 'None';
|
(activityData.length) ? secondsToHours(Math.min(...activityData)) : 'None';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
|
@ -123,49 +123,64 @@ window.addEventListener('load', () => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const tracker = document.getElementById('tracker-wrapper').getAttribute('data-tracker');
|
const target_second = parseInt(document.getElementById('tracker-wrapper').getAttribute('data-second')) + 3;
|
||||||
const target_second = document.getElementById('tracker-wrapper').getAttribute('data-second') + 3;
|
console.log("Target second of refresh: " + target_second);
|
||||||
|
|
||||||
function getSleepTimeSeconds(){
|
function getSleepTimeSeconds() {
|
||||||
// -40 % 60 is -40, which is absolutely wrong and should burn
|
// -40 % 60 is -40, which is absolutely wrong and should burn
|
||||||
var sleepSeconds = (((target_second - new Date().getSeconds()) % 60) + 60) % 60;
|
var sleepSeconds = (((target_second - new Date().getSeconds()) % 60) + 60) % 60;
|
||||||
return sleepSeconds || 60;
|
return sleepSeconds || 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let update_on_view = false;
|
||||||
const update = () => {
|
const update = () => {
|
||||||
const target = $("<div></div>");
|
if (document.hidden) {
|
||||||
console.log("Updating Tracker...");
|
console.log("Document reporting as not visible, not updating Tracker...");
|
||||||
target.load(location.href, function (response, status) {
|
update_on_view = true;
|
||||||
if (status === "success") {
|
} else {
|
||||||
target.find(".table").each(function (i, new_table) {
|
update_on_view = false;
|
||||||
const new_trs = $(new_table).find("tbody>tr");
|
const target = $("<div></div>");
|
||||||
const footer_tr = $(new_table).find("tfoot>tr");
|
console.log("Updating Tracker...");
|
||||||
const old_table = tables.eq(i);
|
target.load(location.href, function (response, status) {
|
||||||
const topscroll = $(old_table.settings()[0].nScrollBody).scrollTop();
|
if (status === "success") {
|
||||||
const leftscroll = $(old_table.settings()[0].nScrollBody).scrollLeft();
|
target.find(".table").each(function (i, new_table) {
|
||||||
old_table.clear();
|
const new_trs = $(new_table).find("tbody>tr");
|
||||||
if (footer_tr.length) {
|
const footer_tr = $(new_table).find("tfoot>tr");
|
||||||
$(old_table.table).find("tfoot").html(footer_tr);
|
const old_table = tables.eq(i);
|
||||||
}
|
const topscroll = $(old_table.settings()[0].nScrollBody).scrollTop();
|
||||||
old_table.rows.add(new_trs);
|
const leftscroll = $(old_table.settings()[0].nScrollBody).scrollLeft();
|
||||||
old_table.draw();
|
old_table.clear();
|
||||||
$(old_table.settings()[0].nScrollBody).scrollTop(topscroll);
|
if (footer_tr.length) {
|
||||||
$(old_table.settings()[0].nScrollBody).scrollLeft(leftscroll);
|
$(old_table.table).find("tfoot").html(footer_tr);
|
||||||
});
|
}
|
||||||
$("#multi-stream-link").replaceWith(target.find("#multi-stream-link"));
|
old_table.rows.add(new_trs);
|
||||||
} else {
|
old_table.draw();
|
||||||
console.log("Failed to connect to Server, in order to update Table Data.");
|
$(old_table.settings()[0].nScrollBody).scrollTop(topscroll);
|
||||||
console.log(response);
|
$(old_table.settings()[0].nScrollBody).scrollLeft(leftscroll);
|
||||||
}
|
});
|
||||||
})
|
$("#multi-stream-link").replaceWith(target.find("#multi-stream-link"));
|
||||||
setTimeout(update, getSleepTimeSeconds()*1000);
|
} else {
|
||||||
|
console.log("Failed to connect to Server, in order to update Table Data.");
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
updater = setTimeout(update, getSleepTimeSeconds() * 1000);
|
||||||
}
|
}
|
||||||
setTimeout(update, getSleepTimeSeconds()*1000);
|
let updater = setTimeout(update, getSleepTimeSeconds() * 1000);
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
adjustTableHeight();
|
adjustTableHeight();
|
||||||
tables.draw();
|
tables.draw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('visibilitychange', () => {
|
||||||
|
if (!document.hidden && update_on_view) {
|
||||||
|
console.log("Page became visible, tracker should be refreshed.");
|
||||||
|
clearTimeout(updater);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
adjustTableHeight();
|
adjustTableHeight();
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
{% include "header/dirtHeader.html" %}
|
{% include "header/dirtHeader.html" %}
|
||||||
{% include "multitrackerNavigation.html" %}
|
{% include "multitrackerNavigation.html" %}
|
||||||
|
|
||||||
<div id="tracker-wrapper" data-tracker="{{ room.tracker | suuid }}">
|
<div id="tracker-wrapper" data-tracker="{{ room.tracker | suuid }}" data-second="{{ saving_second }}">
|
||||||
<div id="tracker-header-bar">
|
<div id="tracker-header-bar">
|
||||||
<input placeholder="Search" id="search" />
|
<input placeholder="Search" id="search" />
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@ import collections
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, NamedTuple, Counter
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, NamedTuple, Counter
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
from email.utils import parsedate_to_datetime
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template, make_response, Response, request
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
|
|
||||||
from MultiServer import Context, get_saving_second
|
from MultiServer import Context, get_saving_second
|
||||||
|
@ -297,46 +298,41 @@ class TrackerData:
|
||||||
return self._multidata.get("spheres", [])
|
return self._multidata.get("spheres", [])
|
||||||
|
|
||||||
|
|
||||||
|
def _process_if_request_valid(incoming_request, room: Optional[Room]) -> Optional[Response]:
|
||||||
|
if not room:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
if_modified = incoming_request.headers.get("If-Modified-Since", None)
|
||||||
|
if if_modified:
|
||||||
|
if_modified = parsedate_to_datetime(if_modified)
|
||||||
|
# if_modified has less precision than last_activity, so we bring them to same precision
|
||||||
|
if if_modified >= room.last_activity.replace(microsecond=0):
|
||||||
|
return make_response("", 304)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>")
|
@app.route("/tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>")
|
||||||
def get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, generic: bool = False) -> str:
|
def get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, generic: bool = False) -> Response:
|
||||||
key = f"{tracker}_{tracked_team}_{tracked_player}_{generic}"
|
key = f"{tracker}_{tracked_team}_{tracked_player}_{generic}"
|
||||||
tracker_page = cache.get(key)
|
response: Optional[Response] = cache.get(key)
|
||||||
if tracker_page:
|
if response:
|
||||||
return tracker_page
|
return response
|
||||||
|
|
||||||
timeout, tracker_page = get_timeout_and_tracker(tracker, tracked_team, tracked_player, generic)
|
|
||||||
cache.set(key, tracker_page, timeout)
|
|
||||||
return tracker_page
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/generic_tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>")
|
|
||||||
def get_generic_game_tracker(tracker: UUID, tracked_team: int, tracked_player: int) -> str:
|
|
||||||
return get_player_tracker(tracker, tracked_team, tracked_player, True)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/tracker/<suuid:tracker>", defaults={"game": "Generic"})
|
|
||||||
@app.route("/tracker/<suuid:tracker>/<game>")
|
|
||||||
@cache.memoize(timeout=TRACKER_CACHE_TIMEOUT_IN_SECONDS)
|
|
||||||
def get_multiworld_tracker(tracker: UUID, game: str):
|
|
||||||
# Room must exist.
|
# Room must exist.
|
||||||
room = Room.get(tracker=tracker)
|
room = Room.get(tracker=tracker)
|
||||||
if not room:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
tracker_data = TrackerData(room)
|
response = _process_if_request_valid(request, room)
|
||||||
enabled_trackers = list(get_enabled_multiworld_trackers(room).keys())
|
if response:
|
||||||
if game not in _multiworld_trackers:
|
return response
|
||||||
return render_generic_multiworld_tracker(tracker_data, enabled_trackers)
|
|
||||||
|
|
||||||
return _multiworld_trackers[game](tracker_data, enabled_trackers)
|
timeout, last_modified, tracker_page = get_timeout_and_player_tracker(room, tracked_team, tracked_player, generic)
|
||||||
|
response = make_response(tracker_page)
|
||||||
|
response.last_modified = last_modified
|
||||||
|
cache.set(key, response, timeout)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def get_timeout_and_tracker(tracker: UUID, tracked_team: int, tracked_player: int, generic: bool) -> Tuple[int, str]:
|
def get_timeout_and_player_tracker(room: Room, tracked_team: int, tracked_player: int, generic: bool)\
|
||||||
# Room must exist.
|
-> Tuple[int, datetime.datetime, str]:
|
||||||
room = Room.get(tracker=tracker)
|
|
||||||
if not room:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
tracker_data = TrackerData(room)
|
tracker_data = TrackerData(room)
|
||||||
|
|
||||||
# Load and render the game-specific player tracker, or fallback to generic tracker if none exists.
|
# Load and render the game-specific player tracker, or fallback to generic tracker if none exists.
|
||||||
|
@ -346,7 +342,48 @@ def get_timeout_and_tracker(tracker: UUID, tracked_team: int, tracked_player: in
|
||||||
else:
|
else:
|
||||||
tracker = render_generic_tracker(tracker_data, tracked_team, tracked_player)
|
tracker = render_generic_tracker(tracker_data, tracked_team, tracked_player)
|
||||||
|
|
||||||
return (tracker_data.get_room_saving_second() - datetime.datetime.now().second) % 60 or 60, tracker
|
return ((tracker_data.get_room_saving_second() - datetime.datetime.now().second)
|
||||||
|
% TRACKER_CACHE_TIMEOUT_IN_SECONDS or TRACKER_CACHE_TIMEOUT_IN_SECONDS, room.last_activity, tracker)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/generic_tracker/<suuid:tracker>/<int:tracked_team>/<int:tracked_player>")
|
||||||
|
def get_generic_game_tracker(tracker: UUID, tracked_team: int, tracked_player: int) -> Response:
|
||||||
|
return get_player_tracker(tracker, tracked_team, tracked_player, True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/tracker/<suuid:tracker>", defaults={"game": "Generic"})
|
||||||
|
@app.route("/tracker/<suuid:tracker>/<game>")
|
||||||
|
def get_multiworld_tracker(tracker: UUID, game: str) -> Response:
|
||||||
|
key = f"{tracker}_{game}"
|
||||||
|
response: Optional[Response] = cache.get(key)
|
||||||
|
if response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Room must exist.
|
||||||
|
room = Room.get(tracker=tracker)
|
||||||
|
|
||||||
|
response = _process_if_request_valid(request, room)
|
||||||
|
if response:
|
||||||
|
return response
|
||||||
|
|
||||||
|
timeout, last_modified, tracker_page = get_timeout_and_multiworld_tracker(room, game)
|
||||||
|
response = make_response(tracker_page)
|
||||||
|
response.last_modified = last_modified
|
||||||
|
cache.set(key, response, timeout)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def get_timeout_and_multiworld_tracker(room: Room, game: str)\
|
||||||
|
-> Tuple[int, datetime.datetime, str]:
|
||||||
|
tracker_data = TrackerData(room)
|
||||||
|
enabled_trackers = list(get_enabled_multiworld_trackers(room).keys())
|
||||||
|
if game in _multiworld_trackers:
|
||||||
|
tracker = _multiworld_trackers[game](tracker_data, enabled_trackers)
|
||||||
|
else:
|
||||||
|
tracker = render_generic_multiworld_tracker(tracker_data, enabled_trackers)
|
||||||
|
|
||||||
|
return ((tracker_data.get_room_saving_second() - datetime.datetime.now().second)
|
||||||
|
% TRACKER_CACHE_TIMEOUT_IN_SECONDS or TRACKER_CACHE_TIMEOUT_IN_SECONDS, room.last_activity, tracker)
|
||||||
|
|
||||||
|
|
||||||
def get_enabled_multiworld_trackers(room: Room) -> Dict[str, Callable]:
|
def get_enabled_multiworld_trackers(room: Room) -> Dict[str, Callable]:
|
||||||
|
@ -416,6 +453,7 @@ def render_generic_multiworld_tracker(tracker_data: TrackerData, enabled_tracker
|
||||||
videos=tracker_data.get_room_videos(),
|
videos=tracker_data.get_room_videos(),
|
||||||
item_id_to_name=tracker_data.item_id_to_name,
|
item_id_to_name=tracker_data.item_id_to_name,
|
||||||
location_id_to_name=tracker_data.location_id_to_name,
|
location_id_to_name=tracker_data.location_id_to_name,
|
||||||
|
saving_second=tracker_data.get_room_saving_second(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue