Support video #73
10 changed files with 189 additions and 132 deletions
|
@ -3,6 +3,53 @@ const config = require('config');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
validateFile (file, name, license, nsfw) {
|
||||||
|
if (!file) {
|
||||||
|
throw new Error('No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null');
|
||||||
|
}
|
||||||
|
// check file type and size
|
||||||
|
switch (file.type) {
|
||||||
|
case 'image/jpeg':
|
||||||
|
case 'image/png':
|
||||||
|
case 'image/gif':
|
||||||
|
if (file.size > 5000000) {
|
||||||
|
throw new Error('Your image exceeds the 5 megabyte limit.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'video/mp4':
|
||||||
|
if (file.size > 50000000) {
|
||||||
|
throw new Error('Your video exceeds the 50 megabyte limit.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate name
|
||||||
|
const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name);
|
||||||
|
if (invalidCharacters) {
|
||||||
|
throw new Error('The name you provided is not allowed. Please use A-Z, a-z, 0-9, and "-" only.');
|
||||||
|
}
|
||||||
|
// validate license
|
||||||
|
if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) {
|
||||||
|
throw new Error('Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch');
|
||||||
|
}
|
||||||
|
switch (nsfw) {
|
||||||
|
case true:
|
||||||
|
case false:
|
||||||
|
case 'true':
|
||||||
|
case 'false':
|
||||||
|
case 'on':
|
||||||
|
case 'off':
|
||||||
|
case 0:
|
||||||
|
case '0':
|
||||||
|
case 1:
|
||||||
|
case '1':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"');
|
||||||
|
}
|
||||||
|
},
|
||||||
createPublishParams (name, filePath, license, nsfw) {
|
createPublishParams (name, filePath, license, nsfw) {
|
||||||
logger.debug(`Creating Publish Parameters for "${name}"`);
|
logger.debug(`Creating Publish Parameters for "${name}"`);
|
||||||
// const payAddress = config.get('WalletConfig.LbryPayAddress');
|
// const payAddress = config.get('WalletConfig.LbryPayAddress');
|
||||||
|
|
|
@ -122,11 +122,22 @@ button.copy-button {
|
||||||
height: 6em;
|
height: 6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#image-preview {
|
#asset-preview-holder {
|
||||||
display: none;
|
width: 100%;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snapshot-generator {
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
left: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 1px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* meme */
|
/* meme */
|
||||||
canvas {
|
canvas {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
|
@ -4,65 +4,27 @@ var uploader = new SocketIOFileUpload(socket);
|
||||||
var stagedFiles = null;
|
var stagedFiles = null;
|
||||||
|
|
||||||
/* configure the submit button */
|
/* configure the submit button */
|
||||||
document.getElementById('publish-submit').addEventListener('click', function(event){
|
function publishSelectedImage(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
// validate inputs
|
||||||
var name = document.getElementById('publish-name').value;
|
var name = document.getElementById('publish-name').value;
|
||||||
var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name);
|
try {
|
||||||
// validate 'name' field
|
validateSubmission(stagedFiles, name);
|
||||||
if (invalidCharacters) {
|
} catch (error) {
|
||||||
alert(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.');
|
alert(error.message);
|
||||||
return;
|
|
||||||
} else if (name.length < 1) {
|
|
||||||
alert("You must enter a name for your claim");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// make sure only 1 file was selected
|
// make sure the name is available then start the upload
|
||||||
if (!stagedFiles) {
|
validateClaimName(name)
|
||||||
alert("Please select a file");
|
.then(function() {
|
||||||
return;
|
uploader.submitFiles(stagedFiles); //note: must pass the file as part of an array.
|
||||||
} else if (stagedFiles.length > 1) {
|
|
||||||
alert("Only one file is allowed at a time");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// make sure the content type is acceptable
|
|
||||||
switch (stagedFiles[0].type) {
|
|
||||||
case "image/png":
|
|
||||||
case "image/jpeg":
|
|
||||||
case "image/gif":
|
|
||||||
case "video/mp4":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
alert("Only .png, .jpeg, .gif, and .mp4 files are currently supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// make sure the name is available
|
|
||||||
var xhttp;
|
|
||||||
xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.open('GET', '/api/isClaimAvailable/' + name, true);
|
|
||||||
xhttp.responseType = 'json';
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 ) {
|
|
||||||
if ( this.status == 200) {
|
|
||||||
if (this.response == true) {
|
|
||||||
uploader.submitFiles(stagedFiles);
|
|
||||||
} else {
|
|
||||||
alert("That name has already been claimed by spee.ch. Please choose a different name.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("request to check claim name failed with status:", this.status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.send();
|
|
||||||
})
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
alert(error);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
/* socketio-file-upload listeners */
|
/* socketio-file-upload listeners */
|
||||||
uploader.maxFileSize = 5000000;
|
|
||||||
uploader.addEventListener("error", function(data){
|
|
||||||
if (data.code === 1) {
|
|
||||||
alert("Sorry, uploading is limitted to 5 megabytes.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
uploader.addEventListener('start', function(event){
|
uploader.addEventListener('start', function(event){
|
||||||
var name = document.getElementById('publish-name').value;
|
var name = document.getElementById('publish-name').value;
|
||||||
var license = document.getElementById('publish-license').value;
|
var license = document.getElementById('publish-license').value;
|
|
@ -3,30 +3,101 @@ function updatePublishStatus(msg){
|
||||||
document.getElementById('publish-status').innerHTML = msg;
|
document.getElementById('publish-status').innerHTML = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateFile(file) {
|
||||||
|
if (!file) {
|
||||||
|
throw new Error('no file provided');
|
||||||
|
}
|
||||||
|
// validate size and type
|
||||||
|
switch (file.type) {
|
||||||
|
case 'image/jpeg':
|
||||||
|
case 'image/png':
|
||||||
|
case 'image/gif':
|
||||||
|
if (file.size > 5000000){
|
||||||
|
throw new Error('Sorry, images are limitted to 5 megabytes.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'video/mp4':
|
||||||
|
if (file.size > 50000000){
|
||||||
|
throw new Error('Sorry, videos are limitted to 50 megabytes.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSubmission(stagedFiles, name){
|
||||||
|
// make sure only 1 file was selected
|
||||||
|
if (!stagedFiles) {
|
||||||
|
throw new Error("Please select a file");
|
||||||
|
} else if (stagedFiles.length > 1) {
|
||||||
|
throw new Error("Only one file is allowed at a time");
|
||||||
|
}
|
||||||
|
// validate 'name' field
|
||||||
|
var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name);
|
||||||
|
if (invalidCharacters) {
|
||||||
|
throw new Error(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.');
|
||||||
|
} else if (name.length < 1) {
|
||||||
|
throw new Error("You must enter a name for your claim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateClaimName (name) {
|
||||||
|
var deferred = new Promise(function(resolve, reject) {
|
||||||
|
var xhttp;
|
||||||
|
xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open('GET', '/api/isClaimAvailable/' + name, true);
|
||||||
|
xhttp.responseType = 'json';
|
||||||
|
xhttp.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 ) {
|
||||||
|
if ( this.status == 200) {
|
||||||
|
if (this.response == true) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject("That name has already been claimed by spee.ch. Please choose a different name.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject("request to check claim name failed with status:" + this.status);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.send();
|
||||||
|
});
|
||||||
|
return deferred;
|
||||||
|
}
|
||||||
|
|
||||||
/* regular publish helper functions */
|
/* regular publish helper functions */
|
||||||
|
|
||||||
function previewAndStageFile(selectedFile){
|
function previewAndStageFile(selectedFile){
|
||||||
var preview = document.getElementById('image-preview');
|
var previewHolder = document.getElementById('asset-preview-holder');
|
||||||
var dropzone = document.getElementById('drop-zone');
|
var dropzone = document.getElementById('drop-zone');
|
||||||
var previewReader = new FileReader();
|
var previewReader = new FileReader();
|
||||||
var nameInput = document.getElementById('publish-name');
|
var nameInput = document.getElementById('publish-name');
|
||||||
|
// validate the file
|
||||||
|
try {
|
||||||
|
validateFile(selectedFile);
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// set the preview
|
||||||
|
if (selectedFile.type === 'video/mp4') {
|
||||||
|
|
||||||
preview.style.display = 'block';
|
} else {
|
||||||
dropzone.style.display = 'none';
|
previewReader.readAsDataURL(selectedFile);
|
||||||
|
|
||||||
previewReader.onloadend = function () {
|
previewReader.onloadend = function () {
|
||||||
preview.src = previewReader.result;
|
dropzone.style.display = 'none';
|
||||||
};
|
previewHolder.style.display = 'block';
|
||||||
|
previewHolder.innerHTML = '<img width="100%" src="' + previewReader.result + '" alt="image preview"/>';
|
||||||
|
|
||||||
if (selectedFile) {
|
};
|
||||||
previewReader.readAsDataURL(selectedFile); // reads the data and sets the img src
|
}
|
||||||
|
// set the name input value to the image name if none is set yet
|
||||||
if (nameInput.value === "") {
|
if (nameInput.value === "") {
|
||||||
nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.'));
|
nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.'));
|
||||||
}
|
}
|
||||||
stagedFiles = [selectedFile]; // stores the selected file for upload
|
// store the selected file for upload
|
||||||
} else {
|
stagedFiles = [selectedFile];
|
||||||
preview.src = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* drop zone functions */
|
/* drop zone functions */
|
||||||
|
@ -64,7 +135,7 @@ function startPublish() {
|
||||||
//download the image
|
//download the image
|
||||||
var dataUrl = canvas.toDataURL('image/jpeg'); // canvas defined in memeDraw.js
|
var dataUrl = canvas.toDataURL('image/jpeg'); // canvas defined in memeDraw.js
|
||||||
var blob = dataURItoBlob(dataUrl)
|
var blob = dataURItoBlob(dataUrl)
|
||||||
var fileName = nameInput.value + ".jpg"; //note: need to dynamically grab type
|
var fileName = nameInput.value + ".jpeg"; //note: need to dynamically grab type
|
||||||
var file = new File([blob], fileName, {type: 'image/jpeg', lastModified: Date.now()});
|
var file = new File([blob], fileName, {type: 'image/jpeg', lastModified: Date.now()});
|
||||||
stageAndPublish(file);
|
stageAndPublish(file);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ const multipart = require('connect-multiparty');
|
||||||
const multipartMiddleware = multipart();
|
const multipartMiddleware = multipart();
|
||||||
const publishController = require('../controllers/publishController.js');
|
const publishController = require('../controllers/publishController.js');
|
||||||
const lbryApi = require('../helpers/libraries/lbryApi.js');
|
const lbryApi = require('../helpers/libraries/lbryApi.js');
|
||||||
const publishHelpers = require('../helpers/libraries/publishHelpers.js');
|
const { createPublishParams, validateFile } = require('../helpers/libraries/publishHelpers.js');
|
||||||
const errorHandlers = require('../helpers/libraries/errorHandlers.js');
|
const errorHandlers = require('../helpers/libraries/errorHandlers.js');
|
||||||
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
||||||
|
|
||||||
|
@ -55,64 +55,30 @@ module.exports = app => {
|
||||||
errorHandlers.handleRequestError('publish', originalUrl, ip, error, res);
|
errorHandlers.handleRequestError('publish', originalUrl, ip, error, res);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// route to run a publish request on the daemon
|
// route to run a publish request on the daemon
|
||||||
app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => {
|
app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => {
|
||||||
// google analytics
|
// google analytics
|
||||||
sendGoogleAnalytics('publish', headers, ip, originalUrl);
|
sendGoogleAnalytics('publish', headers, ip, originalUrl);
|
||||||
// validate that a file was provided
|
// validate that a file was provided
|
||||||
const file = files.speech || files.null;
|
const file = files.speech || files.null;
|
||||||
logger.debug(file);
|
|
||||||
if (!file) {
|
|
||||||
postToStats('publish', originalUrl, ip, 'Error: file');
|
|
||||||
res.status(400).send('Error: No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// check if the size is 5 mb or less
|
|
||||||
if (file.size > 5000000) {
|
|
||||||
res.status(400).send('Error: only files of 5 megabytes or less are allowed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// validate name
|
|
||||||
const name = body.name || file.name.substring(0, file.name.indexOf('.'));
|
const name = body.name || file.name.substring(0, file.name.indexOf('.'));
|
||||||
const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name);
|
|
||||||
if (invalidCharacters) {
|
|
||||||
postToStats('publish', originalUrl, ip, 'Error: name');
|
|
||||||
res.status(400).send('Error: The name you provided is not allowed. Please use A-Z, a-z, 0-9, "_" and "-" only.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// validate license
|
|
||||||
const license = body.license || 'No License Provided';
|
const license = body.license || 'No License Provided';
|
||||||
if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) {
|
|
||||||
postToStats('puplish', originalUrl, ip, 'Error: license');
|
|
||||||
res.status(400).send('Error: Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nsfw = body.nsfw || true;
|
const nsfw = body.nsfw || true;
|
||||||
switch (nsfw) {
|
try {
|
||||||
case true:
|
validateFile(file, name, license, nsfw);
|
||||||
case false:
|
} catch (error) {
|
||||||
case 'true':
|
postToStats('publish', originalUrl, ip, error.message);
|
||||||
case 'false':
|
logger.debug('rejected >>', error.message);
|
||||||
case 'on':
|
res.status(400).send(error.message);
|
||||||
case 'off':
|
|
||||||
case 0:
|
|
||||||
case '0':
|
|
||||||
case 1:
|
|
||||||
case '1':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
postToStats('publish', originalUrl, ip, 'Error: nsfw');
|
|
||||||
res.status(400).send('Error: NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepare the publish parameters
|
||||||
const fileName = file.name;
|
const fileName = file.name;
|
||||||
const filePath = file.path;
|
const filePath = file.path;
|
||||||
const fileType = file.type;
|
const fileType = file.type;
|
||||||
/*
|
const publishParams = createPublishParams(name, filePath, license, nsfw);
|
||||||
note: make sure it's not a harmful file type
|
|
||||||
*/
|
|
||||||
// prepare the publish parameters
|
|
||||||
const publishParams = publishHelpers.createPublishParams(name, filePath, license, nsfw);
|
|
||||||
// publish the file
|
// publish the file
|
||||||
publishController
|
publishController
|
||||||
.publish(publishParams, fileName, fileType)
|
.publish(publishParams, fileName, fileType)
|
||||||
|
|
|
@ -10,4 +10,4 @@
|
||||||
<script src="/siofu/client.js"></script>
|
<script src="/siofu/client.js"></script>
|
||||||
|
|
||||||
<script src="/assets/js/publishFunctions.js"></script>
|
<script src="/assets/js/publishFunctions.js"></script>
|
||||||
<script src="/assets/js/claimPublish.js"></script>
|
<script src="/assets/js/index.js"></script>
|
||||||
|
|
|
@ -10,5 +10,5 @@
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<script src="/siofu/client.js"></script>
|
<script src="/siofu/client.js"></script>
|
||||||
|
|
||||||
<script src="/assets/js/memeDraw.js"></script>
|
<script src="/assets/js/memeFodder-draw.js"></script>
|
||||||
<script src="/assets/js/memePublish.js"></script>
|
<script src="/assets/js/memeFodder-publish.js"></script>
|
|
@ -5,8 +5,8 @@
|
||||||
<p>Drag and drop your file here, or choose your file below.</p>
|
<p>Drag and drop your file here, or choose your file below.</p>
|
||||||
<input type="file" id="siofu_input" name="file" accept="video/*,image/*" onchange="previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
|
<input type="file" id="siofu_input" name="file" accept="video/*,image/*" onchange="previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-preview-holder">
|
<div id="asset-preview-holder">
|
||||||
<img id="image-preview" src="" width="100%" alt="Image preview..."/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-right">
|
<div class="col-right">
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<label for="publish-nsfw">NSFW</label>
|
<label for="publish-nsfw">NSFW</label>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<button id="publish-submit">Publish</button>
|
<button id="publish-submit" onclick="publishSelectedImage(event)">Publish</button>
|
||||||
<a href="/"><button id="publish-reset">Reset</button></a>
|
<a href="/"><button id="publish-reset">Reset</button></a>
|
||||||
</p>
|
</p>
|
||||||
<p><i>By clicking 'Publish' I attest that I have read and agree to the <a href="https://lbry.io/termsofservice" target="_blank">LBRY terms of service</a>.</i></p>
|
<p><i>By clicking 'Publish' I attest that I have read and agree to the <a href="https://lbry.io/termsofservice" target="_blank">LBRY terms of service</a>.</i></p>
|
||||||
|
|
Loading…
Add table
Reference in a new issue