Browse Source

Added ability to upload custom theme

Dmitriy Slipak 4 years ago
parent
commit
bd381d2999

+ 1 - 1
audiogram/draw-frames.js View File

9
       canvases = [];
9
       canvases = [];
10
 
10
 
11
   for (var i = 0; i < 10; i++) {
11
   for (var i = 0; i < 10; i++) {
12
-    canvases.push(new Canvas(options.width, options.height));
12
+    canvases.push(Canvas.createCanvas(options.width, options.height));
13
   }
13
   }
14
 
14
 
15
   for (var i = 0; i < options.numFrames; i++) {
15
   for (var i = 0; i < options.numFrames; i++) {

+ 2 - 1
audiogram/initialize-canvas.js View File

19
       return cb(err);
19
       return cb(err);
20
     }
20
     }
21
 
21
 
22
-    var bg = new Canvas.Image;
22
+    const canvas = Canvas.createCanvas();
23
+    var bg = canvas.Image;
23
     bg.src = raw;
24
     bg.src = raw;
24
     renderer.backgroundImage(bg);
25
     renderer.backgroundImage(bg);
25
 
26
 

+ 95 - 1
client/index.js View File

2
     $ = require("jquery"),
2
     $ = require("jquery"),
3
     preview = require("./preview.js"),
3
     preview = require("./preview.js"),
4
     video = require("./video.js"),
4
     video = require("./video.js"),
5
-    audio = require("./audio.js");
5
+    audio = require("./audio.js"),
6
+    newTheme = null;
6
 
7
 
7
 d3.json("/settings/themes.json", function(err, themes){
8
 d3.json("/settings/themes.json", function(err, themes){
8
 
9
 
90
 
91
 
91
 }
92
 }
92
 
93
 
94
+function uploadTheme() {
95
+  var formData = new FormData();
96
+  var file = preview.newTheme();
97
+
98
+  formData.append("newTheme", file);
99
+
100
+  var newCaption = preview.newCaption();
101
+  console.log('new theme', newCaption);
102
+  formData.append("newCaption", newCaption);
103
+  console.log(formData);
104
+  $.ajax({
105
+    url: "/theme/upload",
106
+    type: "POST",
107
+    data: formData,
108
+    contentType: false,
109
+    cache: false,
110
+    processData: false,
111
+    success: function () {
112
+      d3.json("/settings/themes.json", function(err, themes){
113
+
114
+        var errorMessage;
115
+
116
+        // Themes are missing or invalid
117
+        if (err || !d3.keys(themes).filter(function(d){ return d !== "default"; }).length) {
118
+          if (err instanceof SyntaxError) {
119
+            errorMessage = "Error in settings/themes.json:<br/><code>" + err.toString() + "</code>";
120
+          } else if (err instanceof ProgressEvent) {
121
+            errorMessage = "Error: no settings/themes.json.";
122
+          } else if (err) {
123
+            errorMessage = "Error: couldn't load settings/themes.json.";
124
+          } else {
125
+            errorMessage = "No themes found in settings/themes.json.";
126
+          }
127
+          d3.select("#loading-bars").remove();
128
+          d3.select("#loading-message").html(errorMessage);
129
+          if (err) {
130
+            throw err;
131
+          }
132
+          return;
133
+        }
134
+
135
+        for (var key in themes) {
136
+          themes[key] = $.extend({}, themes.default, themes[key]);
137
+        }
138
+
139
+        preloadImages(themes);
140
+
141
+        d3.select("#input-theme")
142
+          .selectAll("option")
143
+          .each(function (d) {
144
+            if (d.name === newCaption) {
145
+              this["selected"] = "selected";
146
+              d3.select("#input-new-theme").property("value", "");
147
+              d3.select("#input-new-caption").property("value", "");
148
+              return;
149
+            }
150
+          });
151
+
152
+      });
153
+    },
154
+    error: function (error) {
155
+      console.log('error', error);
156
+    }
157
+  });
158
+
159
+}
160
+
93
 function poll(id) {
161
 function poll(id) {
94
 
162
 
95
   setTimeout(function(){
163
   setTimeout(function(){
180
     setClass(null);
248
     setClass(null);
181
   });
249
   });
182
 
250
 
251
+  d3.select("#btn-new-theme").on("click", uploadTheme);
252
+
253
+  d3.select("#input-new-theme").on("change", updateNewThemeFile).each(updateNewThemeFile);
254
+
255
+  d3.select("#input-new-caption").on("change keyup", updateNewCaption).each(updateNewCaption);
256
+
183
   d3.select("#submit").on("click", submitted);
257
   d3.select("#submit").on("click", submitted);
184
 
258
 
185
 }
259
 }
218
 
292
 
219
 }
293
 }
220
 
294
 
295
+function updateNewThemeFile() {
296
+  if (!this.files || !this.files[0]) {
297
+    preview.newTheme(null);
298
+    return true;
299
+  }
300
+
301
+  newTheme = this.files[0];
302
+  preview.loadNewTheme(newTheme, function (err) {
303
+    if (err) {
304
+      setClass("error", "Error updating new theme file");
305
+    } else {
306
+      setClass(null);
307
+    }
308
+  });
309
+}
310
+
221
 function updateCaption() {
311
 function updateCaption() {
222
   preview.caption(this.value);
312
   preview.caption(this.value);
223
 }
313
 }
226
   preview.theme(d3.select(this.options[this.selectedIndex]).datum());
316
   preview.theme(d3.select(this.options[this.selectedIndex]).datum());
227
 }
317
 }
228
 
318
 
319
+function updateNewCaption() {
320
+  preview.newCaption(this.value);
321
+}
322
+
229
 function preloadImages(themes) {
323
 function preloadImages(themes) {
230
 
324
 
231
   // preload images
325
   // preload images

+ 30 - 2
client/preview.js View File

11
 var theme,
11
 var theme,
12
     caption,
12
     caption,
13
     file,
13
     file,
14
-    selection;
14
+    selection,
15
+    newTheme,
16
+    newCaption;
15
 
17
 
16
 function _file(_) {
18
 function _file(_) {
17
   return arguments.length ? (file = _) : file;
19
   return arguments.length ? (file = _) : file;
29
   return arguments.length ? (selection = _) : selection;
31
   return arguments.length ? (selection = _) : selection;
30
 }
32
 }
31
 
33
 
34
+function _newTheme(_) {
35
+  return arguments.length ? (newTheme = _) : newTheme;
36
+}
37
+
38
+function _newCaption(_) {
39
+  return arguments.length ? (newCaption = _) : newCaption;
40
+}
41
+
32
 minimap.onBrush(function(extent){
42
 minimap.onBrush(function(extent){
33
 
43
 
34
   var duration = audio.duration();
44
   var duration = audio.duration();
106
 
116
 
107
 }
117
 }
108
 
118
 
119
+function loadNewTheme(f, cb) {
120
+  d3.queue()
121
+  .await(function(err, data){
122
+
123
+      if (err) {
124
+        return cb(err);
125
+      }
126
+
127
+      newTheme = f;
128
+
129
+      cb(err);
130
+
131
+    });
132
+}
133
+
109
 module.exports = {
134
 module.exports = {
110
   caption: _caption,
135
   caption: _caption,
111
   theme: _theme,
136
   theme: _theme,
112
   file: _file,
137
   file: _file,
113
   selection: _selection,
138
   selection: _selection,
114
-  loadAudio: loadAudio
139
+  loadAudio: loadAudio,
140
+  newTheme: _newTheme,
141
+  newCaption: _newCaption,
142
+  loadNewTheme: loadNewTheme
115
 };
143
 };

+ 21 - 0
editor/index.html View File

36
           </label>
36
           </label>
37
           <input id="input-caption" name="caption" type="text" autocomplete="off" placeholder="Add a caption" />
37
           <input id="input-caption" name="caption" type="text" autocomplete="off" placeholder="Add a caption" />
38
         </div>
38
         </div>
39
+
40
+        <div class="row form-row">
41
+          <hr>
42
+        </div>
43
+        <div class="row form-row" id="row-new-theme">
44
+          <label for="input-new-theme">New theme</label>
45
+          <input id="input-new-theme" name="new-theme" type="file" />
46
+        </div>
47
+        <div class="row form-row" id="row-caption">
48
+          <label for="input-caption">
49
+            Name
50
+          </label>
51
+          <input id="input-new-caption" name="new-caption" type="text" autocomplete="off" placeholder="Add a new theme name" />
52
+        </div>
53
+        <div class="row form-row">
54
+          <button class="btn btn-outline-primary" name="btn-new-theme" id="btn-new-theme">Add</button>
55
+        </div>
56
+        <div class="row form-row">
57
+          <hr>
58
+        </div>
59
+
39
         <div id="preview">
60
         <div id="preview">
40
           <div style="background-color: black;">
61
           <div style="background-color: black;">
41
             <div id="canvas">
62
             <div id="canvas">

File diff suppressed because it is too large
+ 4806 - 0
package-lock.json


+ 3 - 3
package.json View File

25
   "dependencies": {
25
   "dependencies": {
26
     "aws-sdk": "^2.2.39",
26
     "aws-sdk": "^2.2.39",
27
     "browserify": "^13.0.0",
27
     "browserify": "^13.0.0",
28
-    "canvas": "git+https://github.com/chearon/node-canvas.git#12971f64a66b",
28
+    "canvas": "^2.6.1",
29
     "compression": "^1.6.1",
29
     "compression": "^1.6.1",
30
     "d3": "4.10.0",
30
     "d3": "4.10.0",
31
     "dotenv": "^2.0.0",
31
     "dotenv": "^2.0.0",
32
     "express": "^4.13.3",
32
     "express": "^4.13.3",
33
-    "jquery": "^2.2.1",
33
+    "jquery": "^3.4.1",
34
     "mkdirp": "^0.5.1",
34
     "mkdirp": "^0.5.1",
35
     "morgan": "^1.7.0",
35
     "morgan": "^1.7.0",
36
     "multer": "^1.1.0",
36
     "multer": "^1.1.0",
44
     "winston": "^2.2.0"
44
     "winston": "^2.2.0"
45
   },
45
   },
46
   "devDependencies": {
46
   "devDependencies": {
47
-    "supertest": "^1.2.0",
47
+    "supertest": "^4.0.0",
48
     "tape": "^4.6.0",
48
     "tape": "^4.6.0",
49
     "watchify": "^3.7.0"
49
     "watchify": "^3.7.0"
50
   }
50
   }

+ 46 - 1
server/index.js View File

11
     render = require("./render.js"),
11
     render = require("./render.js"),
12
     status = require("./status.js"),
12
     status = require("./status.js"),
13
     fonts = require("./fonts.js"),
13
     fonts = require("./fonts.js"),
14
-    errorHandlers = require("./error.js");
14
+    errorHandlers = require("./error.js"),
15
+    fs = require("fs");;
15
 
16
 
16
 // Settings
17
 // Settings
17
 var serverSettings = require("../lib/settings/");
18
 var serverSettings = require("../lib/settings/");
38
   })
39
   })
39
 };
40
 };
40
 
41
 
42
+var newThemeFileOptions = {
43
+  storage: multer.diskStorage({
44
+    destination: function(req, file, cb) {
45
+
46
+      var dir = path.join(serverSettings.themeStoragePath);
47
+
48
+      mkdirp(dir, function(err) {
49
+        return cb(err, dir);
50
+      });
51
+    },
52
+    filename: function(req, file, cb) {
53
+      cb(null, file.originalname);
54
+    }
55
+  })
56
+};
57
+
41
 if (serverSettings.maxUploadSize) {
58
 if (serverSettings.maxUploadSize) {
42
   fileOptions.limits = {
59
   fileOptions.limits = {
43
     fileSize: +serverSettings.maxUploadSize
60
     fileSize: +serverSettings.maxUploadSize
44
   };
61
   };
62
+  newThemeFileOptions.limits = {
63
+    fileSize: +serverSettings.maxUploadSize
64
+  };
45
 }
65
 }
46
 
66
 
47
 // On submission, check upload, validate input, and start generating a video
67
 // On submission, check upload, validate input, and start generating a video
48
 app.post("/submit/", [multer(fileOptions).single("audio"), render.validate, render.route]);
68
 app.post("/submit/", [multer(fileOptions).single("audio"), render.validate, render.route]);
49
 
69
 
70
+// Upload new theme
71
+app.post("/theme/upload/", [multer(newThemeFileOptions).single("newTheme"), function (req, res) {
72
+  var themesFile = path.join(serverSettings.settingsPath, "themes.json");
73
+  fs.readFile(themesFile, "utf8", function readFileCallback(err, data) {
74
+    if (err) {
75
+      console.log('err', err);
76
+      return null;
77
+    } else {
78
+      var caption = req.body.newCaption;
79
+      var themes = JSON.parse(data);
80
+      themes[caption] = {
81
+        "backgroundImage": req.file.filename
82
+      };
83
+      var jt = JSON.stringify(themes);
84
+      fs.writeFile(themesFile, jt, "utf8", function (err) {
85
+        if (err) {
86
+          console.log(err);
87
+          return null;
88
+        }
89
+      });
90
+    }
91
+  });
92
+  res.end();
93
+}]);
94
+
50
 // If not using S3, serve videos locally
95
 // If not using S3, serve videos locally
51
 if (!serverSettings.s3Bucket) {
96
 if (!serverSettings.s3Bucket) {
52
   app.use("/video/", express.static(path.join(serverSettings.storagePath, "video")));
97
   app.use("/video/", express.static(path.join(serverSettings.storagePath, "video")));

BIN
settings/backgrounds/nyp.png View File


+ 2 - 0
settings/index.js View File

21
 module.exports = {
21
 module.exports = {
22
   workingDirectory: path.join(__dirname, "..", "tmp"),
22
   workingDirectory: path.join(__dirname, "..", "tmp"),
23
   storagePath: path.join(__dirname, "..", "media"),
23
   storagePath: path.join(__dirname, "..", "media"),
24
+  themeStoragePath: path.join(__dirname, "backgrounds"),
25
+  settingsPath: path.join(__dirname, "..", "settings"),
24
   fonts: [
26
   fonts: [
25
     { family: "Source Sans Pro", file: path.join(__dirname, "fonts", "SourceSansPro-Regular.ttf") },
27
     { family: "Source Sans Pro", file: path.join(__dirname, "fonts", "SourceSansPro-Regular.ttf") },
26
     { family: "Source Sans Pro", file: path.join(__dirname, "fonts", "SourceSansPro-Light.ttf"), weight: 300 },
28
     { family: "Source Sans Pro", file: path.join(__dirname, "fonts", "SourceSansPro-Light.ttf"), weight: 300 },

File diff suppressed because it is too large
+ 1 - 105
settings/themes.json


+ 3 - 2
test/frame-test.js View File

71
 
71
 
72
 function checkFrame(test, options) {
72
 function checkFrame(test, options) {
73
 
73
 
74
-  var testCanvas = new Canvas(options.width, options.height),
74
+  var testCanvas = Canvas.createCanvas(options.width, options.height),
75
       context = testCanvas.getContext("2d");
75
       context = testCanvas.getContext("2d");
76
 
76
 
77
   d3.queue()
77
   d3.queue()
81
 
81
 
82
       test.error(e);
82
       test.error(e);
83
 
83
 
84
-      var img = new Canvas.Image;
84
+      const canvas = Canvas.createCanvas();
85
+      var img = canvas.Image;
85
       img.src = f1;
86
       img.src = f1;
86
 
87
 
87
       var bg = getColor(options.backgroundColor || "#fff"),
88
       var bg = getColor(options.backgroundColor || "#fff"),