Pictures!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.js 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. var fs = require("fs");
  2. var http = require("http");
  3. var crypto = require("crypto");
  4. var pathlib = require("path");
  5. var urllib = require("url");
  6. var conf = JSON.parse(fs.readFileSync("conf.json"));
  7. var index = fs.readFileSync("index.html", "utf-8")
  8. .replace(/<<transition_time>>/g, conf.transition_time);
  9. function error(res, err) {
  10. console.trace(err);
  11. res.end(err.toString());
  12. }
  13. function mimetype(path) {
  14. var ext = pathlib.extname(path)
  15. .substring(1)
  16. .toLowerCase();
  17. switch (ext) {
  18. case "html":
  19. case "xml":
  20. return "text/"+ext;
  21. case "png":
  22. case "jpg":
  23. case "jpeg":
  24. case "gif":
  25. return "image/"+ext;
  26. case "svg":
  27. return "image/svg+xml";
  28. case "mov":
  29. case "mp4":
  30. case "ogv":
  31. case "webm":
  32. return "video/"+ext;
  33. case "mp3":
  34. case "ogg":
  35. case "flac":
  36. case "m4a":
  37. return "audio/"+ext;
  38. default:
  39. return "application/octet-stream";
  40. }
  41. }
  42. function sendfile(res, path) {
  43. res.writeHead(200, {
  44. "content-type": mimetype(path)
  45. });
  46. fs.createReadStream(path)
  47. .on("error", err => error(res, err))
  48. .pipe(res);
  49. }
  50. // The individual slide
  51. function Slide(dir) {
  52. var self = {};
  53. self.dir = dir;
  54. self.name = pathlib.parse(dir).name;
  55. self.serveSlide = function(parts, res) {
  56. sendfile(res, pathlib.join(dir, "index.html"));
  57. }
  58. self.serveFiles = function(parts, res) {
  59. // Redirect to / if /{name} is requested
  60. if (parts.pathname.replace(/\//g, "") === self.name) {
  61. res.writeHead(302, { location: "/" });
  62. return res.end();
  63. }
  64. var name = parts.pathname.substring(1).replace(self.name, "");
  65. sendfile(res, pathlib.join(dir, name));
  66. }
  67. self.dirExists = function() {
  68. try {
  69. fs.accessSync(dir, fs.F_OK);
  70. return true;
  71. } catch (err) {
  72. return false;
  73. }
  74. }
  75. return self;
  76. }
  77. // The slideshow, whose job it is to manage all slides
  78. // and tell the client whether it has to update or not
  79. function Slideshow(dir, changeInterval) {
  80. var self = {};
  81. var currentSlide = null;
  82. var awaiters = [];
  83. var slides = [];
  84. self.sendEvent = function(evt, args) {
  85. var str = JSON.stringify({ evt: evt, args: args });
  86. awaiters.forEach(res => res.end(str));
  87. awaiters = [];
  88. }
  89. self.serve = function(req, res) {
  90. var parts = urllib.parse(req.url);
  91. // /: Send the base site to the client
  92. if (parts.pathname === "/") {
  93. res.end(index);
  94. // /init: send initial information about current slide
  95. } else if (parts.pathname === "/init") {
  96. res.end(currentSlide ? currentSlide.name : "");
  97. // /await: long polling, request won't end before a new slide comes
  98. } else if (parts.pathname === "/await") {
  99. awaiters.push(res);
  100. // /slide: serve the current slide's html
  101. } else if (parts.pathname === "/slide" && currentSlide) {
  102. currentSlide.serveSlide(parts, res);
  103. // Serve other files
  104. } else {
  105. var served = false;
  106. for (var slide of slides) {
  107. // If client requests /{slide-name}/*
  108. if (slide.name === parts.pathname.substr(1, slide.name.length)) {
  109. slide.serveFiles(parts, res);
  110. served = true;
  111. break;
  112. }
  113. }
  114. if (!served) {
  115. res.writeHead(404);
  116. res.end("404");
  117. }
  118. }
  119. }
  120. // This function starts the slideshow and goes through the slides
  121. // one by one. When done, it starts again by calling this function again.
  122. function init() {
  123. slides = fs.readdirSync(dir)
  124. .sort()
  125. .map(file => Slide(pathlib.join(dir, file)));
  126. var slideIndex = 0;
  127. currentSlide = slides[slideIndex];
  128. var interval = setInterval(() => {
  129. slideIndex += 1;
  130. // Go to the next slide, or restart
  131. if ((slideIndex >= slides.length)
  132. || (!slides[slideIndex].dirExists())) {
  133. clearInterval(interval);
  134. init();
  135. } else {
  136. currentSlide = slides[slideIndex];
  137. }
  138. // End all awaiting connections to notify slide change
  139. self.sendEvent("next", { name: currentSlide.name });
  140. }, changeInterval);
  141. }
  142. init();
  143. return self;
  144. }
  145. var slideshow = Slideshow(conf.slides, conf.interval);
  146. function onexit() {
  147. slideshow.sendEvent("reload");
  148. process.exit();
  149. }
  150. process.on("exit", onexit);
  151. process.on("SIGINT", onexit);
  152. process.on("SIGTERM", onexit);
  153. var server = http.createServer((req, res) => {
  154. slideshow.serve(req, res);
  155. });
  156. server.on("error", err => {
  157. console.error(err.toString());
  158. system.exit(1);
  159. });
  160. server.listen(conf.port);
  161. console.log("Server running on port "+conf.port+".");