Skip to content

Commit

Permalink
add slideshow feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Kunsi committed Oct 1, 2024
1 parent fb6a140 commit db569d9
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 0 deletions.
28 changes: 28 additions & 0 deletions frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@

socket.setdefaulttimeout(3) # for mqtt

APP_STARTUP_TIME = int(datetime.now().timestamp())


class SubmissionsCollector(Collector):
def collect(self) -> Iterable[Metric]:
Expand Down Expand Up @@ -480,6 +482,32 @@ def metrics():
return generate_latest()


@app.route("/slideshow")
def slideshow():
return render_template("slideshow.jinja", APP_STARTUP_TIME=APP_STARTUP_TIME)


@app.route("/api/slideshow/content")
def api_slideshow_content():
assets = [a.to_dict() for a in get_all_live_assets()]
resp = jsonify(
{
a["id"]: {
"url": a["url"],
"type": a["filetype"],
}
for a in assets
}
)
resp.headers["Cache-Control"] = "public, max-age=30"
return resp


@app.route("/api/startup")
def app_startup_time():
return str(APP_STARTUP_TIME)


# @app.route("/content/last")
# def content_last():
# assets = get_all_live_assets()
Expand Down
26 changes: 26 additions & 0 deletions static/slideshow.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
* {
margin: 0;
padding: 0;
background-color: black;
}

#slideshow, #error {
margin: auto 0;
text-align: center;
}

#error {
font-size: 100px;
margin-top: 200px;
color: white;
font-family: sans-serif;
}

img, video {
display: block;
width: auto;
height: auto;
max-width: 100vw;
max-height: 100vh;
margin: 0 auto;
}
128 changes: 128 additions & 0 deletions static/slideshow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
document.getElementById("slideshow").style.display = "none";

// the list of live assets, as we got it from the api
content = {};

// the list of live asset ids, shuffled at the start of each run
content_shuffled = [];
currently_showing = 0;

function xhr_get(url, callback_func) {
req = new XMLHttpRequest();
req.timeout = 10000;
req.open('GET', url);
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
req.addEventListener('load', function(event) {
if (req.status != 200) {
return;
}

callback_func(event);
});
req.send();
}

// from https://stackoverflow.com/a/12646864
function shuffle_content() {
array = Object.keys(content);
for (let i = array.length - 1; i >= 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
content_shuffled = array;
}

// Auto-reload slideshow in case the backend has restarted. We need this
// to ensure we're running the latest code, without needing to press
// reload on every display individually.
window.setInterval(function() {
console.info('checking if slideshow needs reloading because server has restarted');
xhr_get('/api/startup', function() {
startup = parseInt(req.responseText);
if (startup > 0 && app_startup < startup) {
console.warn('startup time has changed, reloading GUI');
window.location.reload();
} else {
console.info('slideshow does not need reloading');
}
});
}, 42000);

// Load the list of live assets.
function get_live_assets() {
console.info('loading live assets');
xhr_get('/api/slideshow/content', function() {
content = JSON.parse(req.responseText);
console.info("got live assets, " + Object.keys(content).length + " assets in total");
});
}

get_live_assets();
window.setInterval(get_live_assets, 30000);

// The actual magic starts here. This function knows about the current
// position in the slideshow and automatically selects the next available
// picture. If the current picture cannot be found in the list (aka it was
// deleted or expired) or we reach the end of the list, we start again
// from the beginning. In any case, we load the image into a temporary
// element, wait for it to finish loading, then replace the currently
// showing image. This ensures there's always an image showing.
function get_next_asset_to_show() {
if (content_shuffled.length === 0) {
shuffle_content();
}

// iterate over the shuffled content array to find the current asset,
// then return the next one
for (let i = 0; i < content_shuffled.length-1; i++) {
if (currently_showing == content_shuffled[i]) {
next_asset = content_shuffled[i + 1];
currently_showing = next_asset;
return content[next_asset];
}
}

shuffle_content();
next_asset = content_shuffled[0];
currently_showing = next_asset;
return content[next_asset];
}

window.setInterval(function() {
document.getElementById("slideshow").style.display = "block";
document.getElementById("error").style.display = "none";

next_asset = get_next_asset_to_show();
console.info("next asset is " + next_asset['url'] + " of type " + next_asset['type']);

image = document.getElementById("slideshow-image");
video = document.getElementById("slideshow-video");

if (next_asset['type'] == 'image') {
img = document.createElement("img");
img.onload = function() {
video.style.display = "none";
image.style.display = "none";
image.src = this.src;
image.style.display = "block";
}
img.src = next_asset['url'];
} else /* if (next_asset['type'] == 'video') {
vid = document.createElement("video");
vid.onload = function() {
image.style.display = "none";
video.getElementsByTagName("source")[0].src = this.src;
video.style.display = "block";
video.play();
}
vid.src = next_asset['url'];
} else*/ {
/*
document.getElementById("slideshow").style.display = "none";
document.getElementById("error").style.display = "block";
document.getElementById("error-text").innerHTML = "unknown asset type " + next_asset["type"];
*/
console.warn("unknown asset type " + next_asset["type"] + " for asset " + next_asset["url"]);
}
}, 2000);
1 change: 1 addition & 0 deletions templates/layout.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<li {%if request.path=="/faq"%}class="active"{%endif%}><a href="/faq">
FAQ / Contact
</a></li>
<li><a href="{{ url_for("slideshow") }}">Slideshow</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if g.user %}
Expand Down
24 changes: 24 additions & 0 deletions templates/slideshow.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Infobeamer-CMS - Slideshow</title>
<link rel="shortcut icon" href="{{ url_for('static', filename='event-logo.png') }}">
<link rel="stylesheet" href="{{url_for('static', filename='slideshow.css', v=config.VERSION)}}">
</head>
<body>
<div id="slideshow">
<img src="" id="slideshow-image">
<video id="slideshow-video" autoplay muted loop>
<source src="">
</video>
</div>
<div id="error">
<p id="error-text">Loading...</p>
</div>
<script type="text/javascript">
app_startup = {{ APP_STARTUP_TIME }};
</script>
<script type="text/javascript" src="{{url_for('static', filename='slideshow.js', v=config.VERSION)}}"></script>
</html>

0 comments on commit db569d9

Please sign in to comment.