Turn audio into a shareable video. forked from nypublicradio/audiogram

index.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Dependencies
  2. var express = require("express"),
  3. compression = require("compression"),
  4. path = require("path"),
  5. multer = require("multer"),
  6. uuid = require("uuid"),
  7. mkdirp = require("mkdirp");
  8. // Routes and middleware
  9. var logger = require("../lib/logger/"),
  10. render = require("./render.js"),
  11. status = require("./status.js"),
  12. fonts = require("./fonts.js"),
  13. errorHandlers = require("./error.js");
  14. // Settings
  15. var serverSettings = require("../lib/settings/");
  16. var fs = require("fs"),
  17. bodyParser = require("body-parser");
  18. var app = express();
  19. var jsonParser = bodyParser.json();
  20. // var urlencodedParser = bodyParser.urlencoded({ extended: false });
  21. app.use(compression());
  22. app.use(logger.morgan());
  23. const uid = uuid.v1();
  24. // Options for where to store uploaded audio and max size
  25. var fileOptions = {
  26. storage: multer.diskStorage({
  27. destination: function(req, file, cb) {
  28. var dir = path.join(serverSettings.workingDirectory, uid);
  29. mkdirp(dir, function(err) {
  30. return cb(err, dir);
  31. });
  32. },
  33. filename: function(req, file, cb) {
  34. cb(null, file.fieldname);
  35. }
  36. })
  37. };
  38. var newThemeFileOptions = {
  39. storage: multer.diskStorage({
  40. destination: function(req, file, cb) {
  41. var dir = path.join(serverSettings.themeStoragePath);
  42. mkdirp(dir, function(err) {
  43. return cb(err, dir);
  44. });
  45. },
  46. filename: function(req, file, cb) {
  47. cb(null, file.originalname);
  48. }
  49. })
  50. };
  51. if (serverSettings.maxUploadSize) {
  52. fileOptions.limits = {
  53. fileSize: +serverSettings.maxUploadSize
  54. };
  55. newThemeFileOptions.limits = {
  56. fileSize: +serverSettings.maxUploadSize
  57. };
  58. }
  59. // On submission, check upload, validate input, and start generating a video
  60. const mt = multer(fileOptions).fields([{name: 'audio'}, {name: 'subtitle'}]);
  61. app.post("/submit/", [mt, render.validate, render.route]);
  62. // Upload new theme
  63. app.post("/theme/upload/", [multer(newThemeFileOptions).single("newTheme"), function (req, res) {
  64. var themesFile = path.join(serverSettings.settingsPath, "themes.json");
  65. fs.readFile(themesFile, "utf8", function readFileCallback(err, data) {
  66. if (err) {
  67. console.log('err', err);
  68. res.send(JSON.stringify({status: 500, error: err}));
  69. } else {
  70. var caption = req.body.newCaption;
  71. var width = req.body.newWidth;
  72. var height = req.body.newHeight;
  73. var themes = JSON.parse(data);
  74. themes[caption] = {
  75. "backgroundImage": req.file.filename,
  76. "width": parseInt(width),
  77. "height": parseInt(height)
  78. };
  79. var subtitleLeft = (req.body.newSubtitleLeft) ? req.body.newSubtitleLeft : 0;
  80. var subtitleRight = (req.body.newSubtitleRight) ? req.body.newSubtitleRight : 0;
  81. if (subtitleLeft > 0) {
  82. themes[caption]["subtitleLeft"] = parseInt(subtitleLeft);
  83. }
  84. if (subtitleRight > 0) {
  85. themes[caption]["subtitleRight"] = parseInt(subtitleRight);
  86. }
  87. var jt = JSON.stringify(themes);
  88. fs.writeFile(themesFile, jt, "utf8", function (err) {
  89. if (err) {
  90. console.log(err);
  91. res.send(JSON.stringify({status: 500, error: err}));
  92. }
  93. res.send(JSON.stringify({status: 200, success: "success"}));
  94. });
  95. }
  96. });
  97. }]);
  98. // Delete theme
  99. app.post("/theme/delete/", jsonParser, function (req, res) {
  100. var themesFile = path.join(serverSettings.settingsPath, "themes.json");
  101. fs.readFile(themesFile, "utf8", function readFileCallback(err, data) {
  102. if (err) {
  103. console.log('err', err);
  104. res.send(JSON.stringify({status: 500, error: err}));
  105. } else {
  106. var theme = req.body.theme;
  107. var themes = JSON.parse(data);
  108. if (themes[theme]) {
  109. var background = themes[theme]["backgroundImage"];
  110. if (background) {
  111. var asset = path.join(serverSettings.themeStoragePath, background);
  112. try {
  113. fs.unlink(asset, function(err) {
  114. if (err) {
  115. console.log('err', error);
  116. res.send(JSON.stringify({status: 500, error: err}));
  117. }
  118. delete themes[theme];
  119. var jt = JSON.stringify(themes);
  120. fs.writeFile(themesFile, jt, "utf8", function (err) {
  121. if (err) {
  122. console.log(err);
  123. res.send(JSON.stringify({status: 500, error: err}));
  124. }
  125. res.send(JSON.stringify({status: 200, success: "success"}));
  126. });
  127. });
  128. } catch (err) {
  129. console.log(err);
  130. res.send(JSON.stringify({status: 500, error: err}));
  131. }
  132. }
  133. }
  134. }
  135. });
  136. });
  137. // Save theme
  138. app.post("/theme/save/", jsonParser, function (req, res) {
  139. var themesFile = path.join(serverSettings.settingsPath, "themes.json");
  140. fs.readFile(themesFile, "utf8", function readFileCallback(err, data) {
  141. if (err) {
  142. console.log('err', err);
  143. res.send(JSON.stringify({status: 500, error: err}));
  144. } else {
  145. var theme = req.body.theme;
  146. var themes = JSON.parse(data);
  147. themes[theme.name] = theme;
  148. var jt = JSON.stringify(themes);
  149. fs.writeFile(themesFile, jt, "utf8", function (err) {
  150. if (err) {
  151. console.log(err);
  152. res.send(JSON.stringify({status: 500, error: err}));
  153. }
  154. res.send(JSON.stringify({status: 200, success: "success"}));
  155. });
  156. }
  157. });
  158. });
  159. // Theme editor
  160. app.use("/theme/", express.static(path.join(__dirname, "..", "editor/theme.html")));
  161. // If not using S3, serve videos locally
  162. if (!serverSettings.s3Bucket) {
  163. app.use("/video/", express.static(path.join(serverSettings.storagePath, "video")));
  164. }
  165. // Serve custom fonts
  166. app.get("/fonts/fonts.css", fonts.css);
  167. app.get("/fonts/fonts.js", fonts.js);
  168. if (serverSettings.fonts) {
  169. app.get("/fonts/:font", fonts.font);
  170. }
  171. // Check the status of a current video
  172. app.get("/status/:id/", status);
  173. // Serve background images and themes JSON statically
  174. app.use("/settings/", function(req, res, next) {
  175. // Limit to themes.json and bg images
  176. if (req.url.match(/^\/?themes.json$/i) || req.url.match(/^\/?backgrounds\/[^/]+$/i)) {
  177. return next();
  178. }
  179. return res.status(404).send("Cannot GET " + path.join("/settings", req.url));
  180. }, express.static(path.join(__dirname, "..", "settings")));
  181. // Serve editor files statically
  182. app.use(express.static(path.join(__dirname, "..", "editor")));
  183. app.use(errorHandlers);
  184. module.exports = app;