added input for video thumbnail link

This commit is contained in:
bill bittner 2017-10-09 18:29:40 -07:00
parent 58bece6efc
commit 1b37ef91a4
15 changed files with 98 additions and 51 deletions

View file

@ -44,6 +44,7 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
* license (string, optional) * license (string, optional)
* title (string, optional) * title (string, optional)
* description (string, optional) * description (string, optional)
* thumbnail (string, optional) (for .mp4 uploads only)
* channelName(string, optional) * channelName(string, optional)
* channelPassword (string, optional) * channelPassword (string, optional)

View file

@ -16,6 +16,7 @@ module.exports = {
return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); return db.Channel.findOne({where: {channelName: publishParams.channel_name}});
}) })
.then(user => { .then(user => {
logger.debug('user:', user);
let certificateId; let certificateId;
if (user) { if (user) {
certificateId = user.channelClaimId; certificateId = user.channelClaimId;

View file

@ -2,14 +2,14 @@ const axios = require('axios');
const logger = require('winston'); const logger = require('winston');
function handleResponse ({ data }, resolve, reject) { function handleResponse ({ data }, resolve, reject) {
logger.debug('handling lbry api response'); logger.debug('handling lbry api response...');
if (data.result) { if (data.result) {
// check for an error // check for an error
if (data.result.error) { if (data.result.error) {
reject(data.result.error); reject(data.result.error);
return; return;
}; };
logger.debug('data.result', data.result); // logger.debug('data.result', data.result);
resolve(data.result); resolve(data.result);
return; return;
} }

View file

@ -111,7 +111,7 @@ module.exports = {
} }
throw new Error('NSFW must be set to either true or false'); throw new Error('NSFW must be set to either true or false');
}, },
createPublishParams (filePath, name, title, description, license, nsfw, channelName) { createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) {
logger.debug(`Creating Publish Parameters`); logger.debug(`Creating Publish Parameters`);
// provide defaults for title // provide defaults for title
if (title === null || title.trim() === '') { if (title === null || title.trim() === '') {
@ -140,6 +140,10 @@ module.exports = {
}, },
claim_address: config.get('WalletConfig.LbryClaimAddress'), claim_address: config.get('WalletConfig.LbryClaimAddress'),
}; };
// add thumbnail to channel if video
if (thumbnail !== null) {
publishParams['metadata']['thumbnail'] = thumbnail;
}
// add channel to params, if applicable // add channel to params, if applicable
if (channelName) { if (channelName) {
publishParams['channel_name'] = channelName; publishParams['channel_name'] = channelName;

View file

@ -268,20 +268,20 @@ option {
/* modifiers */ /* modifiers */
.input-text--primary, .select--primary, .textarea--primary { .input-text--primary, .select--primary, .textarea--primary {
border-bottom: 1px solid dodgerblue; border-bottom: 1px solid grey;
} }
.input-text--primary:focus, .select--primary:focus, .textarea--primary:focus { .input-text--primary:focus, .select--primary:focus, .textarea--primary:focus {
border-bottom: 1px solid dodgerblue; border-bottom: 1px solid lightgrey;
} }
.input-text--large, .select--large, .textarea--large { .input-text--large, .select--large, .textarea--large {
font-size: 1.5rem; font-size: 1.5rem;
border-left: 1px solid dodgerblue; border-left: 1px solid grey;
} }
.input-text--large:focus, .select--large:focus, .textarea--large:focus { .input-text--large:focus, .select--large:focus, .textarea--large:focus {
border-left: 1px solid dodgerblue; border-left: 1px solid lightgrey;
} }
.input-text--full-width, .textarea--full-width { .input-text--full-width, .textarea--full-width {

View file

@ -84,7 +84,7 @@
width: 100%; width: 100%;
} }
#publish-active-area > .row, #details-detail > .row, #channel-login-form > .row, #publish-channel-form > .row { #publish-active-area > .row, #publish-details > .row, #channel-login-form > .row, #publish-channel-form > .row {
padding: 1em 0px 1em 0px; padding: 1em 0px 1em 0px;
} }
} }

View file

@ -12,14 +12,16 @@ function previewAndStageFile(selectedFile){
const primaryDropzone = document.getElementById('primary-dropzone-wrapper'); const primaryDropzone = document.getElementById('primary-dropzone-wrapper');
const previewReader = new FileReader(); const previewReader = new FileReader();
const nameInput = document.getElementById('claim-name-input'); const nameInput = document.getElementById('claim-name-input');
const fileSelectionError = document.getElementById('input-error-file-selection'); const fileSelectionInputError = document.getElementById('input-error-file-selection');
const thumbnailSelectionTool = document.getElementById('publish-thumbnail');
const thumbnailSelectionInput = document.getElementById('claim-thumbnail-input');
// validate the file's name, type, and size // validate the file's name, type, and size
try { try {
console.log('validating file'); console.log('validating file');
validateFile(selectedFile); validateFile(selectedFile);
} catch (error) { } catch (error) {
console.log('file validation failed with error:', error); console.log('file validation failed with error:', error);
showError(fileSelectionError, error.message); showError(fileSelectionInputError, error.message);
return; return;
} }
// set the image preview, if an image was provided // set the image preview, if an image was provided
@ -32,8 +34,14 @@ function previewAndStageFile(selectedFile){
previewReader.onloadend = function () { previewReader.onloadend = function () {
assetPreview.innerHTML = '<img id="asset-preview" src="' + previewReader.result + '" alt="image preview"/>'; assetPreview.innerHTML = '<img id="asset-preview" src="' + previewReader.result + '" alt="image preview"/>';
}; };
// clear & hide the thumbnail selection input
thumbnailSelectionInput.value = '';
thumbnailSelectionTool.hidden = true;
} else { } else {
assetPreview.innerHTML = `<img id="asset-preview" src="/assets/img/black_video_play.jpg"/>` assetPreview.innerHTML = `<img id="asset-preview" src="/assets/img/black_video_play.jpg"/>`;
// clear & show the thumbnail selection input
thumbnailSelectionInput.value = '';
thumbnailSelectionTool.hidden = false;
} }
// hide the drop zone // hide the drop zone
primaryDropzone.hidden = true; primaryDropzone.hidden = true;
@ -55,12 +63,12 @@ function publishStagedFile(event) {
// declare variables // declare variables
const claimName = document.getElementById('claim-name-input').value; const claimName = document.getElementById('claim-name-input').value;
let channelName = document.getElementById('channel-name-select').value; let channelName = document.getElementById('channel-name-select').value;
const fileSelectionError = document.getElementById('input-error-file-selection'); const fileSelectionInputError = document.getElementById('input-error-file-selection');
const claimNameError = document.getElementById('input-error-claim-name'); const claimNameError = document.getElementById('input-error-claim-name');
const channelSelectError = document.getElementById('input-error-channel-select'); const channelSelectError = document.getElementById('input-error-channel-select');
const publishSubmitError = document.getElementById('input-error-publish-submit'); const publishSubmitError = document.getElementById('input-error-publish-submit');
let anonymousOrInChannel; let anonymousOrInChannel;
// replace channelName with 'anonymous' if needed // replace channelName with 'anonymous' if appropriate
const radios = document.getElementsByName('anonymous-or-channel'); const radios = document.getElementsByName('anonymous-or-channel');
for (let i = 0; i < radios.length; i++) { for (let i = 0; i < radios.length; i++) {
if (radios[i].checked) { if (radios[i].checked) {
@ -70,7 +78,9 @@ function publishStagedFile(event) {
break; break;
} }
} }
if (anonymousOrInChannel === 'anonymous') {channelName = 'anonymous'}; if (anonymousOrInChannel === 'anonymous') {
channelName = null;
};
console.log('channel name:', channelName); console.log('channel name:', channelName);
// validate, submit, and handle response // validate, submit, and handle response
validateFilePublishSubmission(stagedFiles, claimName, channelName) validateFilePublishSubmission(stagedFiles, claimName, channelName)
@ -79,7 +89,7 @@ function publishStagedFile(event) {
}) })
.catch(error => { .catch(error => {
if (error.name === 'FileError') { if (error.name === 'FileError') {
showError(fileSelectionError, error.message); showError(fileSelectionInputError, error.message);
} else if (error.name === 'NameError') { } else if (error.name === 'NameError') {
showError(claimNameError, error.message); showError(claimNameError, error.message);
} else if (error.name === 'ChannelNameError'){ } else if (error.name === 'ChannelNameError'){

View file

@ -25,7 +25,7 @@ module.exports = (app) => {
}); });
}); });
// route to check whether spee.ch has published to a claim // route to check whether spee.ch has published to a claim
app.get('/api/isClaimAvailable/:name', ({ ip, originalUrl, params }, res) => { app.get('/api/isClaimAvailable/:name', ({ params }, res) => {
// send response // send response
checkClaimNameAvailability(params.name) checkClaimNameAvailability(params.name)
.then(result => { .then(result => {
@ -72,7 +72,8 @@ module.exports = (app) => {
}); });
// route to run a publish request on the daemon // route to run a publish request on the daemon
app.post('/api/publish', multipartMiddleware, (req, res) => { app.post('/api/publish', multipartMiddleware, (req, res) => {
logger.debug(req); logger.debug('req:', req);
// validate that mandatory parts of the request are present
const body = req.body; const body = req.body;
const files = req.files; const files = req.files;
try { try {
@ -82,16 +83,14 @@ module.exports = (app) => {
res.status(400).json({success: false, message: error.message}); res.status(400).json({success: false, message: error.message});
return; return;
} }
// required inputs // validate file, name, license, and nsfw
const file = files.file; const file = files.file;
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 name = body.name; const name = body.name;
let nsfw = body.nsfw; let nsfw = body.nsfw;
// cleanse nsfw nsfw = cleanseNSFW(nsfw); // cleanse nsfw
nsfw = cleanseNSFW(nsfw);
// validate file, name, license, and nsfw
try { try {
validatePublishSubmission(file, name, nsfw); validatePublishSubmission(file, name, nsfw);
} catch (error) { } catch (error) {
@ -104,6 +103,7 @@ module.exports = (app) => {
const license = body.license || null; const license = body.license || null;
const title = body.title || null; const title = body.title || null;
const description = body.description || null; const description = body.description || null;
const thumbnail = body.thumbnail || null;
let channelName = body.channelName || null; let channelName = body.channelName || null;
channelName = cleanseChannelName(channelName); channelName = cleanseChannelName(channelName);
const channelPassword = body.channelPassword || null; const channelPassword = body.channelPassword || null;
@ -122,7 +122,7 @@ module.exports = (app) => {
throw new Error('That name is already in use by spee.ch.'); throw new Error('That name is already in use by spee.ch.');
} }
// create publish parameters object // create publish parameters object
return createPublishParams(filePath, name, title, description, license, nsfw, channelName); return createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName);
}) })
.then(publishParams => { .then(publishParams => {
logger.debug('publishParams:', publishParams); logger.debug('publishParams:', publishParams);

View file

@ -9,7 +9,7 @@ module.exports = (app) => {
}); });
// route for log in // route for log in
app.post('/login', passport.authenticate('local-login'), (req, res) => { app.post('/login', passport.authenticate('local-login'), (req, res) => {
logger.debug(req.user); logger.debug('req.user:', req.user);
logger.debug('successful login'); logger.debug('successful login');
res.status(200).json({ res.status(200).json({
success : true, success : true,

View file

@ -35,16 +35,25 @@ module.exports = (app, siofu, hostedContentPath) => {
if (file.success) { if (file.success) {
logger.debug(`Client successfully uploaded ${file.name}`); logger.debug(`Client successfully uploaded ${file.name}`);
socket.emit('publish-status', 'File upload successfully completed. Your image is being published to LBRY (this might take a second)...'); socket.emit('publish-status', 'File upload successfully completed. Your image is being published to LBRY (this might take a second)...');
// /*
/* // NOTE: need to validate that client has the credentials to the channel they chose
NOTE: need to validate that client has the credentials to the channel they chose // otherwise they could circumvent security.
otherwise they could circumvent security client side. // */
*/ let thumbnail;
let channelName = file.meta.channel; if (file.meta.thumbnail) {
if (channelName === 'none') channelName = null; thumbnail = file.meta.thumbnail;
} else {
thumbnail = null;
}
let channelName;
if (file.meta.channel) {
channelName = file.meta.channel;
} else {
channelName = null;
}
// prepare the publish parameters // prepare the publish parameters
const publishParams = createPublishParams(file.pathName, file.meta.name, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw, channelName); const publishParams = createPublishParams(file.pathName, file.meta.name, file.meta.title, file.meta.description, file.meta.license, file.meta.nsfw, thumbnail, channelName);
logger.debug(publishParams); logger.debug('publish parameters:', publishParams);
// publish the file // publish the file
publish(publishParams, file.name, file.meta.type) publish(publishParams, file.name, file.meta.type)
.then(result => { .then(result => {

View file

@ -34,7 +34,7 @@ app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing
app.use(siofu.router); // 'socketio-file-upload' router for uploading with socket.io app.use(siofu.router); // 'socketio-file-upload' router for uploading with socket.io
app.use((req, res, next) => { // custom logging middleware to log all incoming http requests app.use((req, res, next) => { // custom logging middleware to log all incoming http requests
logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`); logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`);
logger.debug(req.body); logger.debug('req.body:', req.body);
next(); next();
}); });

View file

@ -48,6 +48,7 @@
<div id="publish-active-area"> <div id="publish-active-area">
{{> publishForm-Channel}} {{> publishForm-Channel}}
{{> publishForm-Url}} {{> publishForm-Url}}
{{> publishForm-Thumbnail}}
{{> publishForm-Details}} {{> publishForm-Details}}
{{> publishForm-Submit}} {{> publishForm-Submit}}
</div> </div>
@ -107,19 +108,29 @@
document.getElementById('publish-status').innerHTML = msg; document.getElementById('publish-status').innerHTML = msg;
} }
uploader.addEventListener('start', function(event){ uploader.addEventListener('start', function(event){
var name = document.getElementById('claim-name-input').value; console.log('starting upload');
var title = document.getElementById('publish-title').value; const name = document.getElementById('claim-name-input').value;
var description = document.getElementById('publish-description').value; const title = document.getElementById('publish-title').value;
var license = document.getElementById('publish-license').value; const description = document.getElementById('publish-description').value;
var nsfw = document.getElementById('publish-nsfw').checked; const license = document.getElementById('publish-license').value;
var channel = document.getElementById('channel-name-select').value; const nsfw = document.getElementById('publish-nsfw').checked;
const anonymous = document.getElementById('anonymous-select').checked;
console.log('anonymous?', anonymous);
const channel = document.getElementById('channel-name-select').value;
console.log('channel:', channel);
const thumbnail = document.getElementById('claim-thumbnail-input').value;
event.file.meta.name = name; event.file.meta.name = name;
event.file.meta.title = title; event.file.meta.title = title;
event.file.meta.description = description; event.file.meta.description = description;
event.file.meta.license = license; event.file.meta.license = license;
event.file.meta.nsfw = nsfw; event.file.meta.nsfw = nsfw;
event.file.meta.type = stagedFiles[0].type; event.file.meta.type = stagedFiles[0].type;
if (!anonymous) {
event.file.meta.channel = channel; event.file.meta.channel = channel;
}
if (thumbnail && (thumbnail.trim !== '')){
event.file.meta.thumbnail = thumbnail;
}
// hide the submit area // hide the submit area
document.getElementById('publish-active-area').hidden = true; document.getElementById('publish-active-area').hidden = true;
// show the progress status and animation // show the progress status and animation
@ -146,6 +157,7 @@
document.getElementById('publish-progress-bar').hidden = true; document.getElementById('publish-progress-bar').hidden = true;
}); });
socket.on('publish-complete', function(msg){ socket.on('publish-complete', function(msg){
console.log(msg);
var showUrl = msg.result.claim_id + "/" + msg.name; var showUrl = msg.result.claim_id + "/" + msg.name;
// update status // update status
updatePublishStatus('<p>Your publish is complete! You are being redirected to it now.</p><p><a target="_blank" href="' + showUrl + '">If you do not get redirected, click here.</a></p>'); updatePublishStatus('<p>Your publish is complete! You are being redirected to it now.</p><p><a target="_blank" href="' + showUrl + '">If you do not get redirected, click here.</a></p>');

View file

@ -1,4 +1,4 @@
<div id="details-detail" hidden="true"> <div id="publish-details" hidden="true">
<div class="row row--short"> <div class="row row--short">
<div class="column column--3 column--sml-10"> <div class="column column--3 column--sml-10">
@ -22,7 +22,7 @@
<div class="row"> <div class="row">
<div class="column column--10"> <div class="column column--10">
<a class="label" id="details-toggle" href="#" onclick="toggleSection(event)" data-open="false" data-openlabel="[less]" data-closedlabel="[more]" data-slaveelementid="details-detail">[more]</a> <a class="label" id="details-toggle" href="#" onclick="toggleSection(event)" data-open="false" data-openlabel="[less]" data-closedlabel="[more]" data-slaveelementid="publish-details">[more]</a>
</div> </div>
</div> </div>

View file

@ -0,0 +1,10 @@
<div class="row" id="publish-thumbnail" hidden="true">
<div class="column column--3 column--sml-10">
<label class="label">Thumbnail:</label>
</div><div class="column column--6 column--sml-10">
<div class="input-text--primary">
<input type="text" id="claim-thumbnail-input" class="input-text input-text--full-width" placeholder="https://spee.ch/xyz/example.jpg" value="">
</div>
</div>
</div>

View file

@ -6,15 +6,15 @@
<a class="nav-bar-link" href="/">Upload</a> <a class="nav-bar-link" href="/">Upload</a>
<a class="nav-bar-link" href="/popular">Popular</a> <a class="nav-bar-link" href="/popular">Popular</a>
<a class="nav-bar-link" href="/about">About</a> <a class="nav-bar-link" href="/about">About</a>
<!--{{#if user}}--> {{#if user}}
<!--<select type="text" class="select select--no-arrow nav-bar-link" onchange="toggleLogin(event)">--> <select type="text" class="select select--no-arrow nav-bar-link" onchange="toggleLogin(event)">
<!--<option value="{{user.channelName}}:{{user.channelClaimId}}">@{{user.userName}}</option>--> <option value="{{user.channelName}}:{{user.channelClaimId}}">@{{user.userName}}</option>
<!--<option value="view">View</option>--> <option value="view">View</option>
<!--<option value="logout">Logout</option>--> <option value="logout">Logout</option>
<!--</select>--> </select>
<!--{{else}}--> {{else}}
<!--<a class="nav-bar-link" href="/login">Login</a>--> <a class="nav-bar-link" href="/login">Login</a>
<!--{{/if}}--> {{/if}}
</div> </div>
</div> </div>