Short urls 2 #93

Merged
bones7242 merged 8 commits from short-urls-2 into master 2017-07-20 08:16:30 +02:00
14 changed files with 498 additions and 443 deletions

View file

@ -3,151 +3,10 @@ const db = require('../models');
const logger = require('winston');
const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js');
const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js');
const { validateClaimId } = require('../helpers/libraries/serveHelpers.js');
function updateFileIfNeeded (uri, claimName, claimId, localOutpoint, localHeight) {
logger.debug(`Initiating resolve to check outpoint for ${claimName}:${claimId}.`);
// 1. resolve claim
lbryApi
.resolveUri(uri)
.then(result => {
// check to make sure the result is a claim
if (!result.claim) {
logger.debug('resolve did not return a claim');
return;
}
// logger.debug('resolved result:', result);
const resolvedOutpoint = `${result.claim.txid}:${result.claim.nout}`;
const resolvedHeight = result.claim.height;
const resolvedAddress = result.claim.address;
logger.debug('database outpoint:', localOutpoint);
logger.debug('resolved outpoint:', resolvedOutpoint);
// 2. if the outpoint's match, no further work needed
if (localOutpoint === resolvedOutpoint) {
logger.debug('local outpoint matched');
// 2. if the outpoints don't match, check the height
} else if (localHeight > resolvedHeight) {
logger.debug('local height was greater than resolved height');
// 2. get the resolved claim
} else {
logger.debug(`local outpoint did not match for ${uri}. Initiating update.`);
getClaimAndUpdate(uri, resolvedAddress, resolvedHeight);
}
})
.catch(error => {
logger.error(error);
});
}
function getClaimAndUpdate (uri, address, height) {
// 1. get the claim
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
logger.debug(' Get returned outpoint: ', outpoint);
// 2. update the entry in db
db.File
.update({
outpoint,
height, // note: height is coming from the 'resolve', not 'get'.
address, // note: address is coming from the 'resolve', not 'get'.
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
}, {
where: {
name,
claimId: claim_id,
},
})
.then(result => {
logger.debug('successfully updated mysql record', result);
})
.catch(error => {
logger.error('sequelize error', error);
});
})
.catch(error => {
logger.error(`error while getting claim for ${uri} >> `, error);
});
}
function getClaimAndHandleResponse (uri, address, height, resolve, reject) {
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
// create entry in the db
logger.silly(`creating "${name}" record in File db`);
db.File
.create({
name,
claimId : claim_id,
address, // note: comes from parent 'resolve,' not this 'get' call
outpoint,
height, // note: comes from parent 'resolve,' not this 'get' call
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
})
.then(result => {
logger.debug('successfully created mysql record');
})
.catch(error => {
logger.error('sequelize create error', error);
});
// resolve the request
resolve({
name,
claimId : claim_id,
fileName: file_name,
filePath: download_path,
fileType: mime_type,
});
})
.catch(error => {
reject(error);
});
}
function getClaimAndReturnResponse (uri, address, height) {
const deferred = new Promise((resolve, reject) => {
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
// create entry in the db
logger.silly(`creating new File record`);
db.File
.create({
name,
claimId : claim_id,
address, // note: passed as an arguent, not from this 'get' call
outpoint,
height, // note: passed as an arguent, not from this 'get' call
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
})
.then(result => {
logger.debug('successfully created File record');
resolve(result); // note: result.dataValues ?
})
.catch(error => {
logger.error('sequelize create error', error);
reject(error);
});
})
.catch(error => {
reject(error);
});
});
return deferred;
}
const serveHelpers = require('../helpers/libraries/serveHelpers.js');
module.exports = {
getClaimByName (claimName) {
serveClaimByName (claimName) {
const deferred = new Promise((resolve, reject) => {
// 1. get the top free, public claims
getAllFreePublicClaims(claimName)
@ -171,15 +30,14 @@ module.exports = {
// serve the file
resolve(claim.dataValues);
// trigger update if needed
updateFileIfNeeded(uri, name, claimId, claim.dataValues.outpoint, claim.dataValues.height);
serveHelpers.updateFileIfNeeded(uri, claim.dataValues.outpoint, claim.dataValues.height);
// 3. otherwise use daemon to retrieve it
} else {
// get the claim and serve it
getClaimAndHandleResponse(uri, address, height, resolve, reject);
serveHelpers.getClaimAndHandleResponse(uri, address, height, resolve, reject);
}
})
.catch(error => {
logger.error('sequelize error', error);
reject(error);
});
})
@ -189,54 +47,92 @@ module.exports = {
});
return deferred;
},
getClaimByClaimId (name, claimId) {
serveClaimByClaimId (name, claimId) {
logger.debug(`serving claim "${name}" with claimid "${claimId}"`);
const deferred = new Promise((resolve, reject) => {
let uri;
// 1. validate the claim id & retrieve the full claim id if needed
validateClaimId(name, claimId)
.then(validClaimId => {
// 2. check locally for the claim
logger.debug('valid claim id:', validClaimId);
uri = `${name}#${validClaimId}`;
return db.File.findOne({ where: { name, claimId: validClaimId } });
})
// 1. check locally for the claim
const uri = `${name}#${claimId}`;
db.File
.findOne({ where: { name, claimId } })
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
logger.debug('Result found in File table:', result.dataValues);
logger.debug('local result found');
// return the data for the file to be served
resolve(result.dataValues);
// update the file, as needed
updateFileIfNeeded(uri, name, claimId, result.dataValues.outpoint, result.dataValues.outpoint);
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
logger.debug('No result found in File table');
logger.debug('no local result found');
lbryApi
.resolveUri(uri)
.then(result => {
if (!result.claim) { // check to make sure the result is a claim
logger.debug('resolve did not return a claim');
resolve(null);
}
if (isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
resolve(getClaimAndReturnResponse(uri, result.claim.address, result.claim.height));
serveHelpers
.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
resolve(result.dataValues);
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null);
}
})
.catch(error => {
logger.debug('resolve returned an error');
reject(error);
});
}
})
.catch(error => {
reject(error);
});
.catch(error => reject(error));
});
return deferred;
},
getAllClaims (claimName) {
return getAllFreePublicClaims(claimName);
serveClaimByShortUrl (name, shortUrl) {
const deferred = new Promise((resolve, reject) => {
let uri;
let claimId;
// 1. validate the claim id & retrieve the full claim id if needed
serveHelpers
.getClaimIdByShortUrl(name, shortUrl)
.then(result => {
// 2. check locally for the claim
uri = `${name}#${result}`;
claimId = result;
return db.File.findOne({ where: { name, claimId } });
})
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
// return the data for the file to be served
resolve(result.dataValues);
// update the file, as needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
lbryApi
.resolveUri(uri)
.then(result => {
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
serveHelpers
.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
resolve(result.dataValues);
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null);
}
})
.catch(error => reject(error));
}
})
.catch(error => reject(error));
});
return deferred;
},
};

View file

@ -0,0 +1,159 @@
const lbryApi = require('../helpers/libraries/lbryApi.js');
const db = require('../models');
const logger = require('winston');
const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js');
const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js');
const serveHelpers = require('../helpers/libraries/serveHelpers.js');
module.exports = {
showClaimByName (claimName) {
const deferred = new Promise((resolve, reject) => {
// 1. get the top free, public claims
getAllFreePublicClaims(claimName)
.then(freePublicClaimList => {
// check to make sure some claims were found
if (!freePublicClaimList) {
resolve(null);
return;
}
const name = freePublicClaimList[0].name;
const claimId = freePublicClaimList[0].claim_id;
const uri = `${name}#${claimId}`;
const height = freePublicClaimList[0].height;
const address = freePublicClaimList[0].address;
// 2. check to see if the file is available locally
db.File
.findOne({ where: { name, claimId } })
.then(result => {
// 3. if a matching record is found locally, serve it
if (result) {
// return the data for the file to be served
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
// trigger update if needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.height);
// 3. otherwise use daemon to retrieve it
} else {
// get the claim and serve it
serveHelpers.getClaimAndHandleResponse(uri, address, height, resolve, reject);
}
})
.catch(error => {
reject(error);
});
})
.catch(error => {
reject(error);
});
});
return deferred;
},
showClaimByClaimId (name, claimId) {
logger.debug(`Getting claim name: ${name} by claimid: ${claimId}`);
const deferred = new Promise((resolve, reject) => {
// 1. check locally for the claim
const uri = `${name}#${claimId}`;
db.File
.findOne({ where: { name, claimId } })
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
logger.debug('local result found');
// return the data for the file to be served
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
// update the file, as needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
logger.debug('no local result found');
lbryApi
.resolveUri(uri)
.then(result => {
logger.debug('resolve returned successfully');
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
serveHelpers.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
logger.debug('get request returned');
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null, null);
}
})
.catch(error => {
logger.debug('resolve returned an error');
reject(error);
});
}
})
.catch(error => reject(error));
});
return deferred;
},
showClaimByShortUrl (name, shortUrl) {
const deferred = new Promise((resolve, reject) => {
let uri;
let claimId;
// 1. validate the claim id & retrieve the full claim id if needed
serveHelpers.getClaimIdByShortUrl(name, shortUrl)
.then(result => {
// 2. check locally for the claim
uri = `${name}#${result}`;
claimId = result;
return db.File.findOne({ where: { name, claimId } });
})
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
// return the data for the file to be served
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
// update the file, as needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
lbryApi
.resolveUri(uri)
.then(result => {
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
serveHelpers.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
logger.debug('returned');
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null, null);
}
})
.catch(error => reject(error));
}
})
.catch(error => reject(error));
});
return deferred;
},
showAllClaims (claimName) {
return getAllFreePublicClaims(claimName);
},
};

View file

@ -10,11 +10,10 @@ module.exports = {
if (result && (typeof result !== 'string')) {
result = result.toString();
}
// // make sure the ip address(es) are a string
// make sure the ip address(es) are a string
if (ipAddress && (typeof ipAddress !== 'string')) {
ipAddress = ipAddress.toString();
}
logger.silly(name, claimId);
db.File
.findOne({where: { name, claimId }})
.then(file => {
@ -25,7 +24,6 @@ module.exports = {
} else {
FileId = null;
}
logger.silly('file id:', FileId);
return db.Request
.create({
action,

View file

@ -59,6 +59,7 @@ module.exports = {
resolve(data.result);
})
.catch(error => {
logger.debug("axios.post 'get' error");
reject(error);
});
});
@ -90,12 +91,11 @@ module.exports = {
params: { uri },
})
.then(({ data }) => {
// check for errors
if (data.result[uri].error) {
if (data.result[uri].error) { // check for errors
reject(data.result[uri].error);
return;
} else { // if no errors, resolve
resolve(data.result[uri]);
}
resolve(data.result[uri]);
})
.catch(error => {
reject(error);

View file

@ -1,5 +1,74 @@
const logger = require('winston');
const db = require('../../models');
const lbryApi = require('./lbryApi');
function determineShortUrl (claimId, claimList) {
logger.debug('determining short url based on claim id and claim list');
const thisClaim = claimList.filter(claim => { // find this claim in the list & store it
return claim.claim_id === claimId;
})[0];
claimList = claimList.filter(claim => { // remove this claim from the claim list
return claim.claim_id !== claimId;
});
if (claimList.length === 0) { // if there are no other claims, return the first letter of the claim id
return claimId.substring(0, 1);
} else {
let i = 0;
const claimListCopy = claimList;
while (claimList.length !== 0) { // filter out matching claims
i++;
claimList = claimList.filter(claim => {
return (claim.claim_id.substring(0, i) === claimId.substring(0, i));
});
}
i -= 1;
const lastMatch = claimId.substring(0, i);
const matchingClaims = claimListCopy.filter(claim => {
return (claim.claim_id.substring(0, i) === lastMatch);
});
for (let j = 0; j < matchingClaims.length; j++) {
if (matchingClaims[j].height < thisClaim.height) {
return claimId.substring(0, i + 1);
}
}
return claimId.substring(0, i);
}
}
function getClaimAndUpdate (uri, address, height) {
// 1. get the claim
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
logger.debug(' Get returned outpoint: ', outpoint);
// 2. update the entry in db
db.File
.update({
outpoint,
height, // note: height is coming from the 'resolve', not 'get'.
address, // note: address is coming from the 'resolve', not 'get'.
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
}, {
where: {
name,
claimId: claim_id,
},
})
.then(result => {
logger.debug('successfully updated mysql record', result);
})
.catch(error => {
logger.error('sequelize error', error);
});
})
.catch(error => {
logger.error(`error while getting claim for ${uri} >> `, error);
});
}
module.exports = {
serveFile ({ fileName, fileType, filePath }, res) {
@ -29,39 +98,157 @@ module.exports = {
// send the file
res.status(200).sendFile(filePath, options);
},
validateClaimId (name, claimId) {
getClaimIdByShortUrl (name, shortUrl) {
const deferred = new Promise((resolve, reject) => {
logger.debug('claim id length:', claimId.length);
// make sure the claim id is 40 characters
if (claimId.length === 40) {
logger.debug('Claim Id length is valid.');
resolve(claimId);
// if the claim id is shorter than 40, check the db for the full claim id
} else if (claimId.length === 1) {
logger.debug(`Finding claim id for "${name}" "${claimId}"`);
lbryApi.getClaimsList(name)
.then(({ claims }) => {
const regex = new RegExp(`^${shortUrl}`);
logger.debug('regex:', regex);
const filteredClaimsList = claims.filter(claim => {
return regex.test(claim.claim_id);
});
logger.debug('filtered claims list', filteredClaimsList);
switch (filteredClaimsList.length) {
case 0:
reject(new Error('That is an invalid short url'));
break;
case 1:
resolve(filteredClaimsList[0].claim_id);
break;
default:
const sortedClaimsList = filteredClaimsList.sort((a, b) => {
return a.height > b.height;
});
resolve(sortedClaimsList[0].claim_id);
break;
}
})
.catch(error => {
reject(error);
});
});
return deferred;
},
getShortUrlByClaimId (name, claimId) {
const deferred = new Promise((resolve, reject) => {
// get a list of all the claims
lbryApi.getClaimsList(name)
// find the smallest possible unique url for this claim
.then(({ claims }) => {
const shortUrl = determineShortUrl(claimId, claims);
resolve(shortUrl);
})
.catch(error => {
reject(error);
});
});
return deferred;
},
determineShortUrl (claimId, claimList) {
return determineShortUrl(claimId, claimList);
},
updateFileIfNeeded (uri, localOutpoint, localHeight) {
logger.debug(`Initiating resolve to check outpoint for ${uri}`);
// 1. resolve claim
lbryApi
.resolveUri(uri)
.then(result => {
// check to make sure the result is a claim
if (!result.claim) {
logger.debug('resolve did not return a claim');
return;
}
// logger.debug('resolved result:', result);
const resolvedOutpoint = `${result.claim.txid}:${result.claim.nout}`;
const resolvedHeight = result.claim.height;
const resolvedAddress = result.claim.address;
logger.debug('database outpoint:', localOutpoint);
logger.debug('resolved outpoint:', resolvedOutpoint);
// 2. if the outpoint's match, no further work needed
if (localOutpoint === resolvedOutpoint) {
logger.debug('local outpoint matched');
// 2. if the outpoints don't match, check the height
} else if (localHeight > resolvedHeight) {
logger.debug('local height was greater than resolved height');
// 2. get the resolved claim
} else {
logger.debug(`local outpoint did not match for ${uri}. Initiating update.`);
getClaimAndUpdate(uri, resolvedAddress, resolvedHeight);
}
})
.catch(error => {
logger.error(error);
});
},
getClaimAndHandleResponse (uri, address, height, resolve, reject) {
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
// create entry in the db
logger.silly(`creating "${name}" record in File db`);
db.File
.findOne({
where: {
name,
claimId: { $like: `${claimId}%` },
},
.create({
name,
claimId : claim_id,
address, // note: comes from parent 'resolve,' not this 'get' call
outpoint,
height, // note: comes from parent 'resolve,' not this 'get' call
fileName: file_name,
kauffj commented 2017-07-20 16:02:12 +02:00 (Migrated from github.com)
Review

Technically two claims could have the same height, though this is rather unlikely.

Technically two claims could have the same height, though this is rather unlikely.
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
})
.then(file => {
// if no results were found, throw an error
if (!file) {
reject(new Error('That is not a valid short URL.'));
}
// if a result was found, resolve with the full claim id
logger.debug('Full claim id:', file.dataValues.claimId);
resolve(file.dataValues.claimId);
.then(result => {
logger.debug('successfully created mysql record');
})
.catch(error => {
reject(error);
logger.error('sequelize create error', error);
});
} else {
logger.error('The Claim Id was neither 40 nor 1 character in length');
reject(new Error('That Claim Id is not valid.'));
}
// resolve the request
resolve({
name,
claimId : claim_id,
fileName: file_name,
filePath: download_path,
fileType: mime_type,
});
})
.catch(error => {
reject(error);
});
},
getClaimAndReturnResponse (uri, address, height) {
const deferred = new Promise((resolve, reject) => {
lbryApi
.getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
// create entry in the db
logger.silly(`Creating new File record`);
db.File
.create({
name,
claimId : claim_id,
address, // note: passed as an arguent, not from this 'get' call
outpoint,
height, // note: passed as an arguent, not from this 'get' call
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
})
.then(result => {
logger.debug('Successfully created File record');
resolve(result); // note: result.dataValues ?
})
.catch(error => {
logger.debug('db.File.create error');
reject(error);
});
})
.catch(error => {
logger.debug('lbryApi.getClaim error');
reject(error);
});
});
return deferred;
},

View file

@ -1,96 +0,0 @@
var canvas = document.getElementById('meme-canvas');
var img = document.getElementById('start-image');
var canvasWidth;
var canvasHeight;
var fontSize = 28;
var topText = document.getElementById('top-text');
var bottomText = document.getElementById('bottom-text');
var ctx = canvas.getContext('2d');
// create the canvas
img.onload = function() {
// get dimensions of the start img
canvasWidth = img.width;
canvasHeight = img.height;
// hide start image
img.hidden = true;
// size the canvas
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// draw the starting meme
drawMeme()
}
function newCanvas(image){
// hide start image
img = image;
// get dimensions of the start img
canvasHeight = canvasWidth * (img.height / img.width);
// size the canvas
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// draw the meme
drawMeme()
}
// if the text changes, re-draw the meme
topText.addEventListener('keyup', drawMeme);
bottomText.addEventListener('keyup', drawMeme);
// draw the image and draw the text over it
function drawMeme() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);
ctx.lineWidth = 4;
ctx.font = fontSize + 'px sans-serif';
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
var text1 = topText.value;
text1 = text1.toUpperCase();
x = canvasWidth / 2;
y = 5;
wrapText(ctx, text1, x, y, canvasWidth, fontSize, false);
ctx.textBaseline = 'bottom';
var text2 = bottomText.value;
text2 = text2.toUpperCase();
y = canvasHeight - 5;
wrapText(ctx, text2, x, y, canvasHeight, fontSize, true);
}
function wrapText(context, text, x, y, maxWidth, lineHeight, fromBottom) {
var pushMethod = (fromBottom)?'unshift':'push';
lineHeight = (fromBottom)?-lineHeight:lineHeight;
var lines = [];
var y = y;
var line ='';
var words = text.split(' ');
for (var i = 0; i < words.length; i++) {
var testLine = line + ' ' + words[i];
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth) {
lines[pushMethod](line);
line = words[i] + ' ';
} else {
line = testLine;
}
}
lines[pushMethod](line);
for (var k in lines ) {
context.strokeText(lines[k], x, y + lineHeight * k);
context.fillText(lines[k], x, y + lineHeight * k);
}
}

View file

@ -1,50 +0,0 @@
// define variables
var socket = io();
var uploader = new SocketIOFileUpload(socket);
var stagedFiles = null;
var license = 'Creative Commons';
var nsfw = false;
var nameInput = document.getElementById("publish-name");
/* socketio-file-upload listeners */
uploader.addEventListener('start', function(event){
event.file.meta.name = nameInput.value;
event.file.meta.license = license;
event.file.meta.nsfw = nsfw;
event.file.meta.type = stagedFiles[0].type;
// re-set the html in the publish area
document.getElementById('publish-active-area').innerHTML = '<div id="publish-status"></div><div id="progress-bar"></div>';
// start a progress animation
createProgressBar(document.getElementById('progress-bar'), 12);
// google analytics
ga('send', {
hitType: 'event',
eventCategory: 'publish',
eventAction: nameInput.value
});
});
uploader.addEventListener('progress', function(event){
var percent = event.bytesLoaded / event.file.size * 100;
updatePublishStatus('File is ' + percent.toFixed(2) + '% loaded to the server');
});
/* socket.io message listeners */
socket.on('publish-status', function(msg){
updatePublishStatus(msg);
});
socket.on('publish-failure', function(msg){
document.getElementById('publish-active-area').innerHTML = '<p>' + JSON.stringify(msg) + '</p><p> --(✖╭╮✖)→ </p><strong>For help, post the above error text in the #speech channel on the <a href="https://lbry.slack.com/" target="_blank">LBRY slack</a></strong>';
});
socket.on('publish-complete', function(msg){
var publishResults;
var directUrl = '/' + msg.name + '/' + msg.result.claim_id;
// build new publish area
publishResults = '<p>Your publish is complete! View it here:</p>';
publishResults += '<p><a target="_blank" href="' + directUrl + '">spee.ch' + directUrl + '</a></p>';
publishResults += '<p><button class="copy-button">Copy to clipboard</button></p>';
publishResults += '<p><a target="_blank" href="https://explorer.lbry.io/#!/transaction/' + msg.result.txid + '">View the transaction details</a></p>';
publishResults += '<a href="/meme-fodder/play"><button>Reload</button></a></p>';
// update publish area
document.getElementById('publish-active-area').innerHTML = publishResults;
});

View file

@ -78,7 +78,6 @@ module.exports = (app, hostedContentPath) => {
res.status(400).send(error.message);
return;
}
// prepare the publish parameters
const fileName = file.name;
const filePath = file.path;

View file

@ -1,25 +1,38 @@
const logger = require('winston');
const { getClaimByClaimId, getClaimByName } = require('../controllers/serveController.js');
const { serveClaimByName, serveClaimByClaimId, serveClaimByShortUrl } = require('../controllers/serveController.js');
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { serveFile } = require('../helpers/libraries/serveHelpers.js');
function retrieveAssetInfo (name, claimId) {
const deferred = new Promise((resolve, reject) => {
// if claim id is full 40 chars, retrieve the shortest possible url
if (claimId.length === 40) {
resolve(serveClaimByClaimId(name, claimId));
// if the claim id is shorter than 40, retrieve the full claim id & shortest possible url
} else if (claimId.length < 40) {
resolve(serveClaimByShortUrl(name, claimId));
} else {
reject(new Error('That Claim Id is longer than 40 characters.'));
}
});
return deferred;
}
module.exports = (app) => {
// route to serve a specific asset
app.get('/:name/:claim_id', ({ headers, ip, originalUrl, params }, res) => {
// google analytics
sendGoogleAnalytics('serve', headers, ip, originalUrl);
// begin image-serve processes
getClaimByClaimId(params.name, params.claim_id)
.then(fileInfo => {
logger.debug('file info:', fileInfo);
retrieveAssetInfo(params.name, params.claim_id)
.then((fileInfo) => {
// check to make sure a file was found
if (!fileInfo) {
res.status(307).render('noClaims');
return;
}
// serve the file or the show route
if (headers['accept']) { // note: added b/c some requests errored out due to no accept param in header
if (headers['accept']) {
const mimetypes = headers['accept'].split(',');
if (mimetypes.includes('text/html')) {
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
@ -42,7 +55,7 @@ module.exports = (app) => {
// google analytics
sendGoogleAnalytics('serve', headers, ip, originalUrl);
// begin image-serve processes
getClaimByName(params.name)
serveClaimByName(params.name)
.then(fileInfo => {
// check to make sure a file was found
if (!fileInfo) {

View file

@ -1,7 +1,22 @@
const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { getClaimByClaimId, getClaimByName, getAllClaims } = require('../controllers/serveController.js');
const { showClaimByName, showClaimByClaimId, showClaimByShortUrl, showAllClaims } = require('../controllers/showController.js');
const { postToStats, getStatsSummary, getTrendingClaims } = require('../controllers/statsController.js');
function retrieveAssetInfo (name, claimId) {
const deferred = new Promise((resolve, reject) => {
// if claim id is full 40 chars, retrieve the shortest possible url
if (claimId.length === 40) {
resolve(showClaimByClaimId(name, claimId));
// if the claim id is shorter than 40, retrieve the full claim id & shortest possible url
} else if (claimId.length < 40) {
resolve(showClaimByShortUrl(name, claimId));
} else {
reject(new Error('That Claim Id is longer than 40 characters.'));
}
});
return deferred;
}
module.exports = (app) => {
// route to show 'about' page for spee.ch
app.get('/about', ({ ip, originalUrl }, res) => {
@ -34,22 +49,10 @@ module.exports = (app) => {
errorHandlers.handleRequestError(error, res);
});
});
// route to show the meme-fodder meme maker
app.get('/meme-fodder/play', ({ ip, originalUrl }, res) => {
// get and render the content
getAllClaims('meme-fodder')
.then(orderedFreePublicClaims => {
postToStats('show', originalUrl, ip, null, null, 'success');
res.status(200).render('memeFodder', { claims: orderedFreePublicClaims });
})
.catch(error => {
errorHandlers.handleRequestError('show', originalUrl, ip, error, res);
});
});
// route to display all free public claims at a given name
app.get('/:name/all', ({ ip, originalUrl, params }, res) => {
// get and render the content
getAllClaims(params.name)
showAllClaims(params.name)
.then(orderedFreePublicClaims => {
if (!orderedFreePublicClaims) {
res.status(307).render('noClaims');
@ -65,8 +68,9 @@ module.exports = (app) => {
// route to show a specific asset
app.get('/show/:name/:claim_id', ({ ip, originalUrl, params }, res) => {
// begin image-serve processes
getClaimByClaimId(params.name, params.claim_id)
.then(fileInfo => {
retrieveAssetInfo(params.name, params.claim_id)
.then((fileInfo) => {
console.log('SHORT URL:', fileInfo.shortUrl);
// check to make sure a file was found
if (!fileInfo) {
res.status(307).render('noClaims');
@ -77,13 +81,13 @@ module.exports = (app) => {
res.status(200).render('show', { fileInfo });
})
.catch(error => {
errorHandlers.handleRequestError('serve', originalUrl, ip, error, res);
errorHandlers.handleRequestError('show', originalUrl, ip, error, res);
});
});
// route to show the winning free, public claim
app.get('/show/:name', ({ ip, originalUrl, params }, res) => {
// get and render the content
getClaimByName(params.name)
showClaimByName(params.name)
.then(fileInfo => {
// check to make sure a file was found
if (!fileInfo) {
@ -95,7 +99,7 @@ module.exports = (app) => {
res.status(200).render('show', { fileInfo });
})
.catch(error => {
errorHandlers.handleRequestError('serve', originalUrl, ip, error, res);
errorHandlers.handleRequestError('show', originalUrl, ip, error, res);
});
});
};

View file

@ -1,15 +0,0 @@
<div class="wrapper">
{{> topBar}}
<div class="full">
{{> memeFodderMaker}}
{{> memeFodderResults}}
</div>
{{> footer}}
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="/siofu/client.js"></script>
<script src="/assets/js/generalFunctions.js"></script>
<script src="/assets/js/memeFodder-draw.js"></script>
<script src="/assets/js/memeFodder-publish.js"></script>

View file

@ -6,19 +6,21 @@
<h2 class="subheader">Links</h2>
{{!--short direct link to asset--}}
<div class="share-option">
<a href="/{{fileInfo.name}}/{{firstCharacter fileInfo.claimId}}">Direct Link</a>
<div class="input-error" id="input-error-copy-direct-link" hidden="true"></div>
<a href="/{{fileInfo.name}}/{{fileInfo.shortUrl}}">Permanent Short Link</a>
(most convenient)
<div class="input-error" id="input-error-copy-short-link" hidden="true"></div>
<br/>
<input type="text" id="direct-link" class="link" readonly spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{firstCharacter fileInfo.claimId}}" onclick="select()"/>
<button class="copy-button" data-elementtocopy="direct-link" onclick="copyToClipboard(event)">copy</button>
<input type="text" id="short-link" class="link" readonly spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{fileInfo.shortUrl}}" onclick="select()"/>
<button class="copy-button" data-elementtocopy="short-link" onclick="copyToClipboard(event)">copy</button>
</div>
{{!-- link to show route for asset--}}
<div class="share-option">
<a href="/show/{{fileInfo.name}}/{{firstCharacter fileInfo.claimId}}">Details Link</a>
<div class="input-error" id="input-error-copy-show-link" hidden="true"></div>
<a href="/{{fileInfo.name}}/{{fileInfo.claimId}}">Permanent Long Link</a>
(fastest service)
<div class="input-error" id="input-error-copy-long-link" hidden="true"></div>
</br>
<input type="text" id="show-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/show/{{fileInfo.name}}/{{firstCharacter fileInfo.claimId}}"/>
<button class="copy-button" data-elementtocopy="show-link" onclick="copyToClipboard(event)">copy</button>
<input type="text" id="long-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}"/>
<button class="copy-button" data-elementtocopy="long-link" onclick="copyToClipboard(event)">copy</button>
</div>
{{!-- html text for embedding asset--}}
<div class="share-option">

View file

@ -1,31 +0,0 @@
<div class="panel" id="meme-fodder-maker">
<div>
<h2>#LBRYMemeFodder</h2>
<h3>Congratulations, you found the /meme-fodder game!</h3>
<p>Create a meme based on the current <i>lbry://meme-fodder</i> claims using the tool below.</p>
</div>
<div class="col-left">
<canvas id="meme-canvas">
If you can see this, the meme generator is not supported by your browser.
</canvas>
<img id="start-image" src="/meme-fodder" alt="a picture to make your meme with"/>
</div>
<div class="col-right">
<textarea id="direct-link-holder" hidden="true">No URL yet</textarea>
<div id="publish-active-area">
<p>
<label>Meme:</label><br/>
<input id="top-text" type="text" value="Hello" /><br/>
<input id="bottom-text" type="text" value="world!" /><br/>
</p>
<p>
<label for="publish-name">Claim Name:</label></br>
<input id="publish-name" type="text" placeholder="Your claim name" />
</p>
<p>
<button onclick="startPublish()">Save and Publish</button>
</p>
</div>
</div>
<p class="stop-float">Got a masterpiece? <a href="https://twitter.com/hashtag/LBRYMemeFodder" target="_blank">Share it with the community</a> and see what they think!</p>
</div>

View file

@ -1,11 +0,0 @@
<div class="stop-float panel">
<h2>Recent Meme Fodder</h2>
<div class="row">
<p>Below are the free, public images published to <a href="lbry://meme-fodder">lbry://meme-fodder</a>. Want to put a different image on the chopping block? Go publish it!</p>
</div>
<div class="row">
{{#each claims}}
<img class="asset-small" src="/{{this.name}}/{{this.claim_id}}" onclick="newCanvas(this)"/>
{{/each}}
</div>
</div>