diff options
| author | Jules Laplace <jules@okfoc.us> | 2016-04-05 14:05:35 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2016-04-05 14:05:35 -0400 |
| commit | 56580705e3a62eaf1c498c1c0456fc33fe3b2f76 (patch) | |
| tree | e0e42a12aae98aacc733e3f7bb210d72d63c9d19 | |
| parent | cd929e1c38c02c1d048abe702c04c7d4d6188011 (diff) | |
broaden media support
| -rw-r--r-- | app/node_modules/okserver/index.js | 2 | ||||
| -rw-r--r-- | app/node_modules/okservices/oks3/index.js | 54 | ||||
| -rw-r--r-- | examples/db.json | 29 | ||||
| -rw-r--r-- | examples/index.js | 5 | ||||
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | themes/okadmin/public/css/main.css | 3 | ||||
| -rw-r--r-- | themes/okadmin/public/js/app.js | 18 | ||||
| -rw-r--r-- | themes/okadmin/public/js/parser.js | 31 | ||||
| -rw-r--r-- | themes/okadmin/public/js/upload.js | 61 | ||||
| -rw-r--r-- | themes/okadmin/templates/partials/inputs.liquid | 68 |
10 files changed, 226 insertions, 47 deletions
diff --git a/app/node_modules/okserver/index.js b/app/node_modules/okserver/index.js index ee1b4fb..3428565 100644 --- a/app/node_modules/okserver/index.js +++ b/app/node_modules/okserver/index.js @@ -60,7 +60,7 @@ function OKServer(options) { app.use(router); // Add services if (services.s3) { - app.use('/_services/image', services.s3.middleware()); + app.use('/_services/s3', services.s3.middleware()); } if (services.twitter) { app.use('/_services/twitter', services.twitter.middleware()) diff --git a/app/node_modules/okservices/oks3/index.js b/app/node_modules/okservices/oks3/index.js index d556c20..dc0ca19 100644 --- a/app/node_modules/okservices/oks3/index.js +++ b/app/node_modules/okservices/oks3/index.js @@ -4,7 +4,7 @@ var skipperS3 = require('skipper-s3') // Hack to prevent this god-forsaken module from crashing our shit var d = require('domain').create() d.on('error', function (err) { - console.error('Stupid error in S3 upload. Image upload probably prematurely canceled') + console.error('Stupid error in S3 upload. Upload probably prematurely canceled') }) function OKS3(options) { @@ -20,9 +20,9 @@ function OKS3(options) { router.use(skipper()); - router.post('/', function(req, res) { - // req should have a method `file` on it which is - // provided by skipper. Use that to do AWS stuff + // req should have a method `file` on it which is + // provided by skipper. Use that to do AWS stuff + router.post('/image', function(req, res) { d.run(function () { req.file('image').upload({ adapter: skipperS3, @@ -38,7 +38,51 @@ function OKS3(options) { if (err) res.status(500).send(err) res.json(uploadedFiles); }); - }) + }); + }); + + router.post('/audio', function(req, res) { + d.run(function () { + if (! options.s3.allowAudioUploads) { + return res.status(500).json({ error: "audio uploading not permitted" }) + } + req.file('audio').upload({ + adapter: skipperS3, + key: options.s3.key, + secret: options.s3.secret, + bucket: options.s3.bucket, + dirname: options.s3.dirname, + maxBytes: options.s3.maxbytesAudio, + headers: { + 'x-amz-acl': 'public-read' + } + }, function (err, uploadedFiles) { + if (err) res.status(500).send(err) + res.json(uploadedFiles); + }); + }); + }); + + router.post('/video', function(req, res) { + d.run(function () { + if (! options.s3.allowVideoUploads) { + return res.status(500).json({ error: "video uploading not permitted" }) + } + req.file('video').upload({ + adapter: skipperS3, + key: options.s3.key, + secret: options.s3.secret, + bucket: options.s3.bucket, + dirname: options.s3.dirname, + maxBytes: options.s3.maxbytesVideo, + headers: { + 'x-amz-acl': 'public-read' + } + }, function (err, uploadedFiles) { + if (err) res.status(500).send(err) + res.json(uploadedFiles); + }); + }); }); this._middleware = router; diff --git a/examples/db.json b/examples/db.json index 1d045c3..3650355 100644 --- a/examples/db.json +++ b/examples/db.json @@ -174,7 +174,34 @@ "title": "Green", "__index": 1, "dateCreated": "Tue, 05 Apr 2016 15:17:57 GMT", - "media": [] + "media": [ + { + "type": "video", + "token": "https://ltho.s3.amazonaws.com/ee12b137-1c8a-400a-87e3-89cbee7b4da6.mp4", + "uri": "", + "width": "400", + "height": "400", + "title": "ee12b137-1c8a-400a-87e3-89cbee7b4da6.mp4", + "thumb": "http://okfocus.s3.amazonaws.com/misc/okcms/video.png" + }, + { + "type": "youtube", + "token": "y_35kXCQxN4", + "uri": "", + "width": "640", + "height": "360", + "title": "dëf lëöpär¨d¨¨¨¨<>~!@~#!:I!@", + "thumb": "http://i.ytimg.com/vi/y_35kXCQxN4/hqdefault.jpg" + }, + { + "type": "audio", + "token": "http://asdf.us/clouds35.mp3", + "uri": "", + "duration": "225.645792", + "title": "clouds35.mp3", + "thumb": "http://okfocus.s3.amazonaws.com/misc/okcms/video.png" + } + ] } ] }
\ No newline at end of file diff --git a/examples/index.js b/examples/index.js index 81d9241..e9de0a3 100644 --- a/examples/index.js +++ b/examples/index.js @@ -40,6 +40,11 @@ var app = okcms.createApp({ key: process.env.S3_KEY, secret: process.env.S3_SECRET, bucket: process.env.S3_BUCKET, + allowVideoUploads: true, + allowAudioUploads: true, + maxsize: 200, + maxsizeVideo: 150000000, + maxsizeAudio: 150000000, } }, diff --git a/package.json b/package.json index c95a2bc..c3a7280 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "okcms", - "version": "0.1.24", + "version": "0.1.27", "description": "great", "main": "app/index.js", "scripts": { diff --git a/themes/okadmin/public/css/main.css b/themes/okadmin/public/css/main.css index 2f2d376..15b8781 100644 --- a/themes/okadmin/public/css/main.css +++ b/themes/okadmin/public/css/main.css @@ -303,6 +303,7 @@ button, input[type=submit] { width: 15em; height: 6em; } +.main.resource form .audio-element input[type=text], .main.resource form .video-element input[type=text] { width: 15em; } @@ -353,10 +354,12 @@ button, input[type=submit] { margin: 0; padding: 0; cursor: pointer; } +.audio-element:hover .remove, .video-element:hover .remove, .image-element:hover .remove { display: block; } +.audio-element .remove:hover, .video-element .remove:hover, .image-element .remove:hover { color: red; diff --git a/themes/okadmin/public/js/app.js b/themes/okadmin/public/js/app.js index e79f704..a12f517 100644 --- a/themes/okadmin/public/js/app.js +++ b/themes/okadmin/public/js/app.js @@ -5,7 +5,8 @@ var OKAdmin = function(){ var parent = this var uploader = new OKUpload () uploader.bind( this ) - uploader.add = function(url){ + uploader.add = function(media){ + var url = media.url var imageTemplate = $(".image-template", parent).html() var $el = $(imageTemplate) $el.find(".uri").val(url) @@ -46,7 +47,7 @@ var OKAdmin = function(){ $el.find("img").attr("src", url) $("ol", parent).prepend($el) } - uploader.addVideo = function(media){ + uploader.addMedia = function(media){ switch (media.type) { case 'youtube': case 'vimeo': @@ -64,6 +65,19 @@ var OKAdmin = function(){ $el.find("img").attr("src", media.thumbnail ) $("ol", parent).prepend($el) break + case 'audio': + var audioTemplate = $(".audio-template", parent).html() + var $el = $(audioTemplate) + $el.addClass("loaded") + $el.find(".audio-type").val( media.type ) + $el.find(".audio-token").val( media.token ) + $el.find(".audio-uri").val( media.uri ) + $el.find(".audio-title").val( media.title ) + $el.find(".audio-thumb").val( media.thumbnail ) + $el.find(".audio-duration").val( media.duration ) + $el.find("img").attr("src", media.thumbnail ) + $("ol", parent).prepend($el) + break case 'link': var linkTemplate = $(".link-template", parent).html() var $el = $(linkTemplate) diff --git a/themes/okadmin/public/js/parser.js b/themes/okadmin/public/js/parser.js index b4a087d..4ab9a6c 100644 --- a/themes/okadmin/public/js/parser.js +++ b/themes/okadmin/public/js/parser.js @@ -53,6 +53,31 @@ var Parser = { return '<video src="' + media.url + '">'; } }, { + type: 'audio', + regex: /\.(wav|mp3)(\?.*)?$/i, + fetch: function(url, done) { + var audio = document.createElement("audio") + var url_parts = url.replace(/\?.*$/, "").split("/") + var filename = url_parts[ url_parts.length-1 ] + audio.addEventListener("loadedmetadata", function(){ + var duration = audio.duration + audio = null + done({ + url: url, + type: "audio", + token: url, + thumbnail: "http://okfocus.s3.amazonaws.com/misc/okcms/audio.png", + title: filename, + duration: duration, + }) + }) + audio.src = url + audio.load() + }, + tag: function (media) { + return '<audio src="' + media.url + '">'; + } + }, { type: 'youtube', regex: /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i, fetch: function(url, done) { @@ -116,8 +141,7 @@ var Parser = { // return '<img class="video" type="vimeo" vid="'+media.token+'" src="'+media.thumbnail+'"><span class="playvid">▶</span>'; return '<div class="video" style="width: ' + media.width + 'px; height: ' + media.height + 'px; overflow: hidden; position: relative;"><iframe frameborder="0" scrolling="no" seamless="seamless" webkitallowfullscreen="webkitAllowFullScreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen" id="okplayer" src="http://player.vimeo.com/video/' + media.token + '?api=1&title=0&byline=0&portrait=0&playbar=0&player_id=okplayer&loop=0&autoplay=0" width="' + media.width + '" height="' + media.height + '" style="position: absolute; top: 0px; left: 0px; width: ' + media.width + 'px; height: ' + media.height + 'px;"></iframe></div>' } - }, - { + }, { type: 'soundcloud', regex: /soundcloud.com\/[-a-zA-Z0-9]+\/[-a-zA-Z0-9]+\/?$/i, fetch: function (url, done) { @@ -128,13 +152,14 @@ var Parser = { + '&client_id=' + '0673fbe6fc794a7750f680747e863b10', success: function(result) { - // console.log(result) + console.log(result) done({ url: url, type: "soundcloud", token: result.id, thumbnail: result.artwork_url || result.user.avatar_url, title: result.user.username + " - " + result.title, + duration: result.duration, width: 166, height: 166, }) diff --git a/themes/okadmin/public/js/upload.js b/themes/okadmin/public/js/upload.js index cfc1199..2ce14ad 100644 --- a/themes/okadmin/public/js/upload.js +++ b/themes/okadmin/public/js/upload.js @@ -1,6 +1,8 @@ var OKUpload = function(){ - this.action = "/_services/image" + this.action = this.imageAction = "/_services/s3/image" + this.videoAction = "/_services/s3/video" + this.audioAction = "/_services/s3/audio" } OKUpload.prototype.bind = function(rapper){ var uploader = this @@ -14,18 +16,25 @@ OKUpload.prototype.bind = function(rapper){ $(".add-url", rapper).on("keydown blur", pressEnter( function(e){ var url = $(this).val() $(this).val("") - if (! url) return - Parser.parse( url, function(media){ - console.log(url, media) - if (media.type == "image") { - uploader.add(url) - } - else { - uploader.addVideo(media) - } - }) + uploader.parse(url) })) } +OKUpload.prototype.parse = function(url){ + if (! url) return + var uploader = this + Parser.parse( url, function(media){ + console.log(url, media) + if (! media) { + alert("Not a valid link") + } + else if (media.type == "image") { + uploader.add(media) + } + else { + uploader.addMedia(media) + } + }) +} OKUpload.prototype.handleFileSelect = function(e) { e.stopPropagation(); e.preventDefault(); @@ -33,13 +42,29 @@ OKUpload.prototype.handleFileSelect = function(e) { var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; for (var i = 0, f; f = files[i]; i++) { - if ( ! f.type.match('image.*')) { + if ( ! f.type.match('image.*') && ! f.type.match('video.*') ) { continue; } this.upload(f) } } OKUpload.prototype.upload = function(f){ + + var field, action + + if ( f.type.match('video.*') ) { + field = 'video' + action = this.videoAction + } + else if ( f.type.match('audio.*') ) { + field = 'audio' + action = this.audioAction + } + else { + field = 'image' + action = this.imageAction || this.action + } + this.xhrCount += 1 this.$progress.addClass("loading") @@ -51,10 +76,10 @@ OKUpload.prototype.upload = function(f){ this.$progress.append($loader) var fd = new FormData() - fd.append('image', f) + fd.append(field, f) var request = new XMLHttpRequest() - request.open("POST", this.action, true) + request.open("POST", action, true) request.addEventListener("progress", updateProgress.bind(this)); request.addEventListener("load", transferComplete.bind(this)); @@ -77,6 +102,7 @@ OKUpload.prototype.upload = function(f){ this.success( responseData ) } catch (e) { + console.log(request.responseText) console.log("ERROR PARSING JSON") } } @@ -121,12 +147,15 @@ OKUpload.prototype.success = function(data){ return } var url = data[0].extra.Location - this.add(url) + this.parse(url) } OKUpload.prototype.add = function(media){ console.log(media) } -OKUpload.prototype.addVideo = function(media){ +OKUpload.prototype.addMedia = function(media){ + console.log(media) +} +OKUpload.prototype.addAudio = function(media){ console.log(media) } OKUpload.prototype.error = function(error){ diff --git a/themes/okadmin/templates/partials/inputs.liquid b/themes/okadmin/templates/partials/inputs.liquid index e618c61..0cd7f6b 100644 --- a/themes/okadmin/templates/partials/inputs.liquid +++ b/themes/okadmin/templates/partials/inputs.liquid @@ -9,14 +9,14 @@ {% if type == 'string' %} <input - name="{{name}}" type="text" value="{{spec.value}}"> + name="{{name}}" type="text" value="{{spec.value | escape}}"> {% elsif type == 'text' %} <textarea - name="{{name}}">{{spec.value}}</textarea> + name="{{name}}">{{spec.value | escape}}</textarea> {% elsif type == 'number' %} <input type="number" - name="{{name}}" value="{{spec.value}}"> + name="{{name}}" value="{{spec.value | escape}}"> {% elsif type == 'enum' or type == 'foreign-key' %} <select name="{{name}}"> @@ -32,9 +32,9 @@ <input name="{{name}}[width]" value="{{spec.value.width}}" type="hidden" class="video-width" hidden> <input name="{{name}}[height]" value="{{spec.value.height}}" type="hidden" class="video-height" hidden> <label>Title</label> - <input name="{{name}}[title]" type="text" value="{{spec.value.title}}" class="video-title"> + <input name="{{name}}[title]" type="text" value="{{spec.value.title | escape}}" class="video-title"> <label>Thumbnail</label> - <input name="{{name}}[thumb]" type="text" value="{{spec.value.thumb}}" class="video-thumb"> + <input name="{{name}}[thumb]" type="text" value="{{spec.value.thumb | escape}}" class="video-thumb"> </div> {% elsif type == 'image' %} <div class="image group {% if spec.value.uri %}loaded{% endif %}"> @@ -47,8 +47,8 @@ </div> <div class="image-element"> <input class="uri" type="hidden" name="{{name}}[uri]" value="{{spec.value.uri}}"> - <textarea class="caption" name="{{name}}[caption]">{{spec.value.caption}}</textarea> - <img src="{{spec.value.uri}}" alt="{{spec.value.caption}}"> + <textarea class="caption" name="{{name}}[caption]">{{spec.value.caption | escape}}</textarea> + <img src="{{spec.value.uri}}" alt="{{spec.value.caption | escape}}"> <button class="remove">x</button> </div> </div> @@ -78,7 +78,7 @@ {% elsif type == 'tag-list' %} <div class="tag-list"> <input name="{{name}}" - value="{{spec.value}}" + value="{{spec.value | escape}}" placeholder="Enter a comma separated list of tags."> </div> @@ -90,13 +90,13 @@ <div class="handle"></div> <input name="{{name}}[{{forloop.index0}}][text]" - value="{{link.text}}" + value="{{link.text | escape}}" type="text" placeholder="Link text" class="link-input link-text"> <input name="{{name}}[{{forloop.index0}}][uri]" - value="{{link.uri}}" + value="{{link.uri | escape}}" type="text" placeholder="URL" class="link-input link-uri"> @@ -142,8 +142,8 @@ <div class="media-list group loaded"> <div class="fields"> <div class="add-image-button"> - <input type="file" accept="image/*" multiple> - <button>+ Add images</button> + <input type="file" accept="image/*,video/*,audio/*" multiple> + <button>+ Add media</button> </div> <input class="add-url" type="text" placeholder="+ Add Image/Video/Link URL"> </div> @@ -177,6 +177,23 @@ </li> </script> + <script type="text/html" class="audio-template"> + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[][type]" type="hidden" class="audio-type" hidden> + <input name="{{name}}[][token]" type="hidden" class="audio-token" hidden> + <input name="{{name}}[][uri]" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[][title]" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[][thumb]" type="text" class="audio-thumb"> + </div> + <img> + <button class="remove">x</button> + </li> + </script> + <script type="text/html" class="link-template"> <li class="link-element"> <input class="uri" type="text" name="{{name}}[][uri]" value=""> @@ -197,19 +214,34 @@ <input name="{{name}}[{{forloop.index0}}][width]" value="{{image.width}}" type="hidden" class="video-width" hidden> <input name="{{name}}[{{forloop.index0}}][height]" value="{{image.height}}" type="hidden" class="video-height" hidden> <label>Caption</label> - <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title}}" type="text" class="video-title"> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="video-title"> <label>Thumbnail</label> <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="video-thumb"> </div> <img src="{{image.thumb}}"> <button class="remove">x</button> </li> + {% elsif image.type and (image.type == "audio" or image.type == "soundcloud") %} + <li class="audio-element"> + <div style="float: left"> + <input name="{{name}}[{{forloop.index0}}][type]" value="{{image.type}}" type="hidden" class="audio-type" hidden> + <input name="{{name}}[{{forloop.index0}}][token]" value="{{image.token}}" type="hidden" class="audio-token" hidden> + <input name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}" type="hidden" class="audio-uri" hidden> + <input name="{{name}}[{{forloop.index0}}][duration]" value="{{image.duration}}" type="hidden" class="audio-duration" hidden> + <label>Caption</label> + <input name="{{name}}[{{forloop.index0}}][title]" value="{{image.title | escape}}" type="text" class="audio-title"> + <label>Thumbnail</label> + <input name="{{name}}[{{forloop.index0}}][thumb]" value="{{image.thumb}}" type="text" class="audio-thumb"> + </div> + <img src="{{image.thumb}}"> + <button class="remove">x</button> + </li> {% elsif image.type and image.type == "link" %} <li class="link-element"> <label>URL</label> <input class="uri" type="text" name="{{name}}[][uri]" value="{{image.uri}}"> <label>Caption</label> - <textarea class="caption" name="{{name}}[][caption]" placeholder="Caption">{{image.caption}}</textarea> + <textarea class="caption" name="{{name}}[][caption]" placeholder="Caption">{{image.caption | escape}}</textarea> <input type="hidden" name="{{name}}[][type]" value="link"> <button class="remove">x</button> </li> @@ -217,7 +249,7 @@ <li class="image-element"> <label>Caption</label> <input type="hidden" name="{{name}}[][uri]" value="{{image.uri}}"> - <textarea class="caption" name="{{name}}[][caption]">{{image.caption}}</textarea> + <textarea class="caption" name="{{name}}[][caption]">{{image.caption | escape}}</textarea> <input type="hidden" name="{{name}}[][type]" value="image"> <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> <button class="remove">x</button> @@ -249,7 +281,7 @@ {% for image in spec.value %} <li class="image-element"> <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> - <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption}}</textarea> + <textarea class="caption" name="{{name}}[{{forloop.index0}}][caption]">{{image.caption | escape}}</textarea> <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> <button class="remove">x</button> </li> @@ -282,8 +314,8 @@ <img src="{{image.uri}}" alt="{{image.caption | strip_html}}"> <button class="remove">x</button> <input type="hidden" name="{{name}}[{{forloop.index0}}][uri]" value="{{image.uri}}"> - <input class="caption" name="{{name}}[{{forloop.index0}}][label]" value="{{image.label}}" placeholder="Name"> - <input class="caption" name="{{name}}[{{forloop.index0}}][caption]" value="{{image.caption}}" placeholder="Email"> + <input class="caption" name="{{name}}[{{forloop.index0}}][label]" value="{{image.label | escape}}" placeholder="Name"> + <input class="caption" name="{{name}}[{{forloop.index0}}][caption]" value="{{image.caption | escape}}" placeholder="Email"> </li> {% endfor %} </ol> |
