@@ -1,59 +0,0 @@ | |||
#!/bin/sh | |||
REMOTE='http://localhost:8080' | |||
INTERVAL=1 | |||
requirecmd() | |||
{ | |||
which "$1" 2>1 > /dev/null | |||
if [ "$?" -ne 0 ]; then | |||
echo "Missing command: $1" | |||
exit 1 | |||
fi | |||
} | |||
requirecmd curl | |||
requirecmd fbv | |||
requirecmd shasum | |||
calchash() | |||
{ | |||
shasum --algorithm 1 "$1" 2>/dev/null | cut -d ' ' -f 1 | |||
} | |||
PICFILE="pic" | |||
PICHASH=$(calchash "$PICFILE") | |||
PROGPID=$$ | |||
# Loop responsible for requesting to the server | |||
# and updating the picture if necessary | |||
requestloop() | |||
{ | |||
while :; do | |||
URL="$REMOTE/$PICHASH" | |||
curl --silent "$URL" > refresh | |||
# Empty response means the file hasn't changed | |||
if [ $(file refresh | cut -d ' ' -f 2) = "empty" ]; then | |||
rm refresh | |||
# If the response is non-empty, we received an image file. | |||
# We move that file to $PICFILE, then display it | |||
else | |||
mv refresh "$PICFILE" | |||
PICHASH=$(calchash "$PICFILE") | |||
pkill --parent "$PROGPID" fbv | |||
fi | |||
sleep "$INTERVAL" | |||
done | |||
} | |||
requestloop & | |||
while :; do | |||
if [ -f "$PICFILE" ]; then | |||
fbv --noclear --noinfo "$PICFILE" | |||
else | |||
sleep 2; | |||
fi | |||
done |
@@ -0,0 +1,4 @@ | |||
{ | |||
"slides": "slides", | |||
"interval": 5000 | |||
} |
@@ -0,0 +1,101 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Slides</title> | |||
<style> | |||
html, body { | |||
margin: 0px; | |||
padding: 0px; | |||
height: 100%; | |||
overflow: hidden; | |||
} | |||
#_overlay { | |||
z-index: 2; | |||
} | |||
#_main { | |||
z-index: 1; | |||
} | |||
._content { | |||
background: white; | |||
position: absolute; | |||
width: 100%; | |||
height: 100%; | |||
top: 0px; | |||
left: 0px; | |||
} | |||
._content { | |||
text-align: center; | |||
} | |||
._content h1 { font-size: 5em } | |||
._content h2 { font-size: 4.5em } | |||
._content h3 { font-size: 4em } | |||
._content p { font-size: 2.2em } | |||
._content .fullscreen { | |||
position: absolute; | |||
width: auto; | |||
height: 100%; | |||
top: 0px; | |||
left: 50%; | |||
-moz-transform: translateX(-50%); | |||
-ms-transform: translateX(-50%); | |||
-webkit-transform: translateX(-50%); | |||
transform: translateX(-50%); | |||
} | |||
#_overlay { | |||
transition: opacity 1s; | |||
opacity: 1; | |||
} | |||
#_overlay.hidden { | |||
opacity: 0; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<div id="_main" class="_content"></div> | |||
<div id="_overlay" class="_content"></div> | |||
<script> | |||
var overlay = () => document.querySelector("#_overlay"); | |||
var main = () => document.querySelector("#_main"); | |||
// Swap the IDs of two elements | |||
function swap(elem1, elem2) { | |||
var tmp = elem1.id; | |||
elem1.id = elem2.id; | |||
elem2.id = tmp; | |||
} | |||
// Change slides with a transition | |||
function update() { | |||
overlay().innerHTML = ""; | |||
overlay().className = "_content"; | |||
swap(main(), overlay()); | |||
fetch("/slide") | |||
.then(response => response.text()) | |||
.then(text => { | |||
setTimeout(() => { | |||
main().innerHTML = text; | |||
overlay().className = "_content hidden"; | |||
}, 1000); | |||
}) | |||
.catch(err => console.error(err)); | |||
// Wait for the next slide change, then update again | |||
fetch("/await") | |||
.then(response => update()) | |||
.catch(err => { console.error(err); update(); }); | |||
} | |||
update(); | |||
</script> | |||
</body> | |||
</html> |
@@ -0,0 +1,112 @@ | |||
var fs = require("fs"); | |||
var http = require("http"); | |||
var crypto = require("crypto"); | |||
var pathlib = require("path"); | |||
var urllib = require("url"); | |||
var index = fs.readFileSync("index.html"); | |||
var conf = JSON.parse(fs.readFileSync("conf.json")); | |||
function error(res, err) { | |||
console.trace(err); | |||
console.log(res.toString()); | |||
} | |||
// The individual slide | |||
function Slide(dir) { | |||
var self = {}; | |||
self.dir = dir; | |||
function serve(parts, res) { | |||
if (parts.pathname === "/slide") { | |||
fs.createReadStream(pathlib.join(dir, "index.html")) | |||
.on("error", err => error(res, err)) | |||
.pipe(res); | |||
} else { | |||
console.log("loading "+parts.pathname+" from "+dir); | |||
fs.createReadStream(pathlib.join(dir, parts.pathname)) | |||
.on("error", err => error(res, err)) | |||
.pipe(res); | |||
} | |||
} | |||
self.serve = function(parts, res) { | |||
try { | |||
serve(parts, res); | |||
} catch (err) { | |||
if (err.code && err.code === "ENOENT") | |||
res.writeHead(404); | |||
error(res, err); | |||
} | |||
} | |||
return self; | |||
} | |||
// The slideshow, whose job it is to manage all slides | |||
// and tell the client whether it has to update or not | |||
function Slideshow(dir, changeInterval) { | |||
var self = {}; | |||
var currentSlide = null; | |||
var awaiters = []; | |||
self.serve = function(req, res) { | |||
console.log(currentSlide ? currentSlide.dir : ""); | |||
var parts = urllib.parse(req.url); | |||
// /: Send the base site to the client | |||
if (parts.pathname === "/") { | |||
res.end(index); | |||
// /await: long polling, request won't end before a new slide comes | |||
} else if (parts.pathname === "/await") { | |||
awaiters.push(res); | |||
// There's a current slide: leave serving files up to the slide | |||
} else if (currentSlide) { | |||
currentSlide.serve(parts, res); | |||
// There's no current slide show | |||
} else { | |||
res.end("No current slideshow."); | |||
} | |||
} | |||
// This function starts the slideshow and goes through the slides | |||
// one by one. When done, it starts again by calling this function again. | |||
function init() { | |||
var slides = fs.readdirSync(dir) | |||
.sort() | |||
.map(file => Slide(pathlib.join(dir, file))); | |||
var slideIndex = 0; | |||
currentSlide = slides[slideIndex]; | |||
var interval = setInterval(() => { | |||
slideIndex += 1; | |||
// Go to the next slide, or restart | |||
if (slideIndex >= slides.length) { | |||
clearInterval(interval); | |||
init(); | |||
} else { | |||
currentSlide = slides[slideIndex]; | |||
} | |||
// End all awaiting connections to notify slide change | |||
awaiters.forEach(res => res.end()); | |||
}, changeInterval); | |||
} | |||
init(); | |||
return self; | |||
} | |||
var slideshow = Slideshow(conf.slides, conf.interval); | |||
http.createServer((req, res) => { | |||
slideshow.serve(req, res); | |||
}).listen(8080); |
@@ -1,29 +0,0 @@ | |||
var fs = require("fs"); | |||
var http = require("http"); | |||
var crypto = require("crypto"); | |||
function Pic(path) { | |||
var content = fs.readFileSync(path); | |||
var hash = crypto.createHash("sha1").update(content).digest("hex"); | |||
var self = { | |||
path: path, | |||
hash: hash, | |||
content: content | |||
} | |||
return self; | |||
} | |||
var currentPic = Pic("/home/martin/background.jpg"); | |||
http.createServer((req, res) => { | |||
var hash = req.url.substring(1); // Remove the first / to get only hash | |||
console.log("got hash "+hash+", existing hash is "+currentPic.hash); | |||
if (hash === currentPic.hash) { | |||
res.end(); | |||
} else { | |||
res.end(currentPic.content); | |||
} | |||
}).listen(8080); |
@@ -0,0 +1,3 @@ | |||
<h1> Viktig informasjon om greier </h1> | |||
<p> Et eller annet </p> |
@@ -0,0 +1 @@ | |||
<h1> This is the second slide </h1> |
@@ -0,0 +1 @@ | |||
<h1> This is the third slide </h1> |
@@ -0,0 +1 @@ | |||
<img class="fullscreen" src="image.svg"> |