|
|
@@ -4,218 +4,73 @@ var crypto = require("crypto"); |
|
|
|
var pathlib = require("path"); |
|
|
|
var urllib = require("url"); |
|
|
|
|
|
|
|
var Slideshow = require("./js/slideshow"); |
|
|
|
var error = require("./js/error"); |
|
|
|
|
|
|
|
var conf = JSON.parse(fs.readFileSync("conf.json")); |
|
|
|
var index = fs.readFileSync("index.html", "utf-8") |
|
|
|
.replace(/<<transition_time>>/g, conf.transition_time); |
|
|
|
|
|
|
|
function error(res, err) { |
|
|
|
console.trace(err); |
|
|
|
res.end(err.toString()); |
|
|
|
} |
|
|
|
|
|
|
|
function mimetype(path) { |
|
|
|
var ext = pathlib.extname(path) |
|
|
|
.substring(1) |
|
|
|
.toLowerCase(); |
|
|
|
|
|
|
|
switch (ext) { |
|
|
|
|
|
|
|
case "html": |
|
|
|
case "xml": |
|
|
|
return "text/"+ext; |
|
|
|
|
|
|
|
case "png": |
|
|
|
case "jpg": |
|
|
|
case "jpeg": |
|
|
|
case "gif": |
|
|
|
return "image/"+ext; |
|
|
|
|
|
|
|
case "svg": |
|
|
|
return "image/svg+xml"; |
|
|
|
|
|
|
|
case "mov": |
|
|
|
case "mp4": |
|
|
|
case "ogv": |
|
|
|
case "webm": |
|
|
|
return "video/"+ext; |
|
|
|
|
|
|
|
case "mp3": |
|
|
|
case "ogg": |
|
|
|
case "flac": |
|
|
|
case "m4a": |
|
|
|
return "audio/"+ext; |
|
|
|
|
|
|
|
default: |
|
|
|
return "application/octet-stream"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function sendfile(res, path) { |
|
|
|
res.writeHead(200, { |
|
|
|
"content-type": mimetype(path) |
|
|
|
}); |
|
|
|
|
|
|
|
fs.createReadStream(path) |
|
|
|
.on("error", err => error(res, err)) |
|
|
|
.pipe(res); |
|
|
|
} |
|
|
|
|
|
|
|
// The individual slide |
|
|
|
function Slide(dir) { |
|
|
|
var self = {}; |
|
|
|
|
|
|
|
self.dir = dir; |
|
|
|
self.name = pathlib.parse(dir).name; |
|
|
|
|
|
|
|
self.serveSlide = function(parts, res) { |
|
|
|
sendfile(res, pathlib.join(dir, "index.html")); |
|
|
|
} |
|
|
|
|
|
|
|
self.serveFiles = function(parts, res) { |
|
|
|
|
|
|
|
// Redirect to / if /{name} is requested |
|
|
|
if (parts.pathname.replace(/\//g, "") === self.name) { |
|
|
|
res.writeHead(302, { location: "/" }); |
|
|
|
return res.end(); |
|
|
|
} |
|
|
|
|
|
|
|
var name = parts.pathname.substring(1).replace(self.name, ""); |
|
|
|
sendfile(res, pathlib.join(dir, name)); |
|
|
|
} |
|
|
|
|
|
|
|
self.indexExists = function() { |
|
|
|
try { |
|
|
|
fs.accessSync(pathlib.join(dir, "index.html")); |
|
|
|
return true; |
|
|
|
} catch (err) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
var slideshow = Slideshow(conf.slides, conf.interval); |
|
|
|
|
|
|
|
return self; |
|
|
|
function onexit(code) { |
|
|
|
console.log("exiting", code); |
|
|
|
slideshow.sendEvent("reload"); |
|
|
|
process.exit(); |
|
|
|
} |
|
|
|
process.on("exit", onexit); |
|
|
|
process.on("SIGINT", onexit); |
|
|
|
process.on("SIGTERM", onexit); |
|
|
|
|
|
|
|
// 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 = []; |
|
|
|
var slides = []; |
|
|
|
var slideIndex = 0; |
|
|
|
var nextTimeout; |
|
|
|
|
|
|
|
self.sendEvent = function(evt, args) { |
|
|
|
var str = JSON.stringify({ evt: evt, args: args }); |
|
|
|
awaiters.forEach(res => res.end(str)); |
|
|
|
awaiters = []; |
|
|
|
} |
|
|
|
|
|
|
|
self.serve = function(req, res) { |
|
|
|
var parts = urllib.parse(req.url); |
|
|
|
process.on("uncaughtException", onexit); |
|
|
|
|
|
|
|
// /: Send the base site to the client |
|
|
|
if (parts.pathname === "/") { |
|
|
|
res.end(index); |
|
|
|
function handler(req, res) { |
|
|
|
var parts = urllib.parse(req.url); |
|
|
|
|
|
|
|
// /polyfills.js: JavaScript polyfills |
|
|
|
} else if (parts.pathname === "/polyfills.js") { |
|
|
|
fs.createReadStream("polyfills.js") |
|
|
|
.pipe(res) |
|
|
|
.on("error", err => res.end(err.toString())); |
|
|
|
// /: Send the base site to the client |
|
|
|
if (parts.pathname === "/") { |
|
|
|
res.end(index); |
|
|
|
|
|
|
|
// /init: send initial information about current slide |
|
|
|
} else if (parts.pathname === "/init") { |
|
|
|
res.end(currentSlide ? currentSlide.name : ""); |
|
|
|
// /polyfills.js: JavaScript polyfills |
|
|
|
} else if (parts.pathname === "polyfills.js") { |
|
|
|
fs.createReadStream("polyfills.js") |
|
|
|
.pipe(res) |
|
|
|
.on("error", err => error(err, res)); |
|
|
|
|
|
|
|
// /await: long polling, request won't end before a new slide comes |
|
|
|
} else if (parts.pathname === "/await") { |
|
|
|
awaiters.push(res); |
|
|
|
// /init: send initial information about current slide |
|
|
|
} else if (parts.pathname === "/init") { |
|
|
|
res.end(slideshow.getSlideName()); |
|
|
|
|
|
|
|
// /slide: serve the current slide's html |
|
|
|
} else if (parts.pathname === "/slide" && currentSlide) { |
|
|
|
currentSlide.serveSlide(parts, res); |
|
|
|
// /await: long polling, request won't end before a new slide comes |
|
|
|
} else if (parts.pathname === "/await") { |
|
|
|
slideshow.pushAwaiter(res); |
|
|
|
|
|
|
|
// Serve other files |
|
|
|
} else { |
|
|
|
var served = false; |
|
|
|
// /slide: serve the current slide's html |
|
|
|
} else if (parts.pathname === "/slide") { |
|
|
|
slideshow.serveCurrentSlide(parts, res); |
|
|
|
|
|
|
|
for (var slide of slides) { |
|
|
|
// Serve other files |
|
|
|
} else { |
|
|
|
var served = false; |
|
|
|
|
|
|
|
// If client requests /{slide-name}/* |
|
|
|
if (slide.name === parts.pathname.substr(1, slide.name.length)) { |
|
|
|
slide.serveFiles(parts, res); |
|
|
|
served = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
for (var slide of slideshow.getSlides()) { |
|
|
|
|
|
|
|
if (!served) { |
|
|
|
res.writeHead(404); |
|
|
|
res.end("404"); |
|
|
|
// If client requests /{slide-name}/* |
|
|
|
if (slide.name === parts.pathname.substr(1, slide.name.length)) { |
|
|
|
slide.serveFiles(parts, res); |
|
|
|
served = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function next() { |
|
|
|
slideIndex += 1; |
|
|
|
|
|
|
|
// Go to the next slide, or restart |
|
|
|
if ((slideIndex >= slides.length)) { |
|
|
|
clearTimeout(nextTimeout); |
|
|
|
init(); |
|
|
|
} else { |
|
|
|
currentSlide = slides[slideIndex]; |
|
|
|
nextTimeout = setTimeout(next, changeInterval); |
|
|
|
} |
|
|
|
|
|
|
|
// End all awaiting connections to notify slide change, |
|
|
|
if (currentSlide.indexExists()) { |
|
|
|
self.sendEvent("next", { name: currentSlide.name }); |
|
|
|
|
|
|
|
// Or go to the next slide if the current one doesn't have an index.html |
|
|
|
} else { |
|
|
|
clearTimeout(nextTimeout); |
|
|
|
setTimeout(next, 0); |
|
|
|
if (!served) { |
|
|
|
res.writeHead(404); |
|
|
|
res.end("404"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self.next = next; |
|
|
|
|
|
|
|
// 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() { |
|
|
|
slides = fs.readdirSync(dir) |
|
|
|
.sort() |
|
|
|
.map(file => Slide(pathlib.join(dir, file))); |
|
|
|
|
|
|
|
slideIndex = 0; |
|
|
|
currentSlide = slides[slideIndex]; |
|
|
|
|
|
|
|
nextTimeout = setTimeout(next, changeInterval); |
|
|
|
} |
|
|
|
init(); |
|
|
|
|
|
|
|
return self; |
|
|
|
} |
|
|
|
|
|
|
|
var slideshow = Slideshow(conf.slides, conf.interval); |
|
|
|
|
|
|
|
function onexit(code) { |
|
|
|
console.log("exiting", code); |
|
|
|
slideshow.sendEvent("reload"); |
|
|
|
process.exit(); |
|
|
|
} |
|
|
|
process.on("exit", onexit); |
|
|
|
process.on("SIGINT", onexit); |
|
|
|
process.on("SIGTERM", onexit); |
|
|
|
|
|
|
|
process.on("uncaughtException", onexit); |
|
|
|
|
|
|
|
var server = http.createServer((req, res) => { |
|
|
|
slideshow.serve(req, res); |
|
|
|
}); |
|
|
|
var server = http.createServer(handler); |
|
|
|
server.on("error", err => { |
|
|
|
console.error(err); |
|
|
|
system.exit(1); |