Pictures!
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

server.js 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. var slideIndex = 0;
  85. var nextTimeout;
  86. self.sendEvent = function(evt, args) {
  87. var str = JSON.stringify({ evt: evt, args: args });
  88. awaiters.forEach(res => res.end(str));
  89. awaiters = [];
  90. }
  91. self.serve = function(req, res) {
  92. var parts = urllib.parse(req.url);
  93. // /: Send the base site to the client
  94. if (parts.pathname === "/") {
  95. res.end(index);
  96. // /polyfills.js: JavaScript polyfills
  97. } else if (parts.pathname === "/polyfills.js") {
  98. fs.createReadStream("polyfills.js")
  99. .pipe(res)
  100. .on("error", err => res.end(err.toString()));
  101. // /init: send initial information about current slide
  102. } else if (parts.pathname === "/init") {
  103. res.end(currentSlide ? currentSlide.name : "");
  104. // /await: long polling, request won't end before a new slide comes
  105. } else if (parts.pathname === "/await") {
  106. awaiters.push(res);
  107. // /slide: serve the current slide's html
  108. } else if (parts.pathname === "/slide" && currentSlide) {
  109. currentSlide.serveSlide(parts, res);
  110. // Serve other files
  111. } else {
  112. var served = false;
  113. for (var slide of slides) {
  114. // If client requests /{slide-name}/*
  115. if (slide.name === parts.pathname.substr(1, slide.name.length)) {
  116. slide.serveFiles(parts, res);
  117. served = true;
  118. break;
  119. }
  120. }
  121. if (!served) {
  122. res.writeHead(404);
  123. res.end("404");
  124. }
  125. }
  126. }
  127. function next() {
  128. slideIndex += 1;
  129. // Go to the next slide, or restart
  130. if ((slideIndex >= slides.length)
  131. || (!slides[slideIndex].dirExists())) {
  132. clearTimeout(nextTimeout);
  133. init();
  134. } else {
  135. currentSlide = slides[slideIndex];
  136. nextTimeout = setTimeout(next, changeInterval);
  137. }
  138. // End all awaiting connections to notify slide change
  139. self.sendEvent("next", { name: currentSlide.name });
  140. }
  141. self.next = next;
  142. // This function starts the slideshow and goes through the slides
  143. // one by one. When done, it starts again by calling this function again.
  144. function init() {
  145. slides = fs.readdirSync(dir)
  146. .sort()
  147. .map(file => Slide(pathlib.join(dir, file)));
  148. slideIndex = 0;
  149. currentSlide = slides[slideIndex];
  150. nextTimeout = setTimeout(next, changeInterval);
  151. }
  152. init();
  153. return self;
  154. }
  155. var slideshow = Slideshow(conf.slides, conf.interval);
  156. function onexit(code) {
  157. console.log("exiting", code);
  158. slideshow.sendEvent("reload");
  159. process.exit();
  160. }
  161. process.on("exit", onexit);
  162. process.on("SIGINT", onexit);
  163. process.on("SIGTERM", onexit);
  164. process.on("uncaughtException", onexit);
  165. var server = http.createServer((req, res) => {
  166. slideshow.serve(req, res);
  167. });
  168. server.on("error", err => {
  169. console.error(err);
  170. system.exit(1);
  171. });
  172. server.listen(conf.port);
  173. console.log("Server running on port "+conf.port+".");