rebuilt the /claim_id/claim route (no update yet)

This commit is contained in:
bill bittner 2017-08-01 18:58:13 -07:00
parent 75d4164af9
commit 238f2af63f
9 changed files with 239 additions and 137 deletions

View file

@ -5,15 +5,139 @@ const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicCla
const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js'); const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js');
const serveHelpers = require('../helpers/libraries/serveHelpers.js'); const serveHelpers = require('../helpers/libraries/serveHelpers.js');
function checkForLocalAssetByShortUrl (channel, name) {
return new Promise((resolve, reject) => {
db.File
.findOne({where: { name, channel }})
.then(result => {
if (result) {
resolve(result.dataValues);
} else {
resolve(null);
}
})
.catch(error => {
reject(error);
});
});
}
function checkForLocalAssetByChannel (channelName, name) {
return new Promise((resolve, reject) => {
});
}
function checkForLocalAssetByClaimId (claimId, name) {
return new Promise((resolve, reject) => {
db.File
.findOne({where: { name, claimId }})
.then(result => {
if (result) {
resolve(result.dataValues);
} else {
resolve(null);
}
})
.catch(error => {
reject(error);
});
});
}
function formatGetResultsToFileInfo ({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) {
return {
name,
claimId : claim_id,
outpoint,
fileName: file_name,
filePath: download_path,
fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw,
};
}
module.exports = { module.exports = {
getAssetByChannel (channelName, name) { getAssetByChannel (channelName, name) {
return new Promise((resolve, reject) => {
// check locally for claim
checkForLocalAssetByChannel(channelName, name)
.then(result => {
// if not found locally, make a get request
if (!result) {
resolve();
// if a result was found, resolve the result
} else {
resolve(result);
}
})
.catch(error => {
reject(error);
});
});
}, },
getAssetByShortUrl (shortUrl, name) { getAssetByShortUrl (shortUrl, name) {
return new Promise((resolve, reject) => {
// check locally for claim
checkForLocalAssetByShortUrl(shortUrl, name)
.then(result => {
// if not found locally, make a get request
if (!result) {
resolve();
// if a result was found, resolve the result
} else {
resolve(result);
}
})
.catch(error => {
reject(error);
});
});
}, },
getAssetByClaimId (fullClaimId, name) { getAssetByClaimId (fullClaimId, name) {
return new Promise((resolve, reject) => {
// 1. check locally for claim
checkForLocalAssetByClaimId(fullClaimId, name)
.then(dataValues => {
// 2. if a result was found, resolve the result
if (dataValues) {
logger.debug('found a local file for this claimId');
// trigger an update check for the file
serveHelpers.updateFileIfNeeded(dataValues.name, dataValues.claimId, dataValues.outpoint, dataValues.height);
// resolve promise
resolve(dataValues);
// 2. if not found locally, make a get request
} else {
logger.debug('no local file for this claimId');
// 3. resolve the claim
lbryApi.resolveUri(`${name}#${fullClaimId}`)
.then(resolveResult => {
// if the claim is free and public, then get it
if (resolveResult.claim && isFreePublicClaim(resolveResult.claim)) {
lbryApi.getClaim(`${name}#${fullClaimId}`)
.then(getResult => {
let fileInfo = formatGetResultsToFileInfo(getResult);
fileInfo['address'] = resolveResult.claim.address;
fileInfo['height'] = resolveResult.claim.height;
resolve(fileInfo);
})
.catch(error => {
reject(error);
});
// if not, resolve with no claims
} else {
resolve(null);
}
})
.catch(error => {
reject(error);
});
}
})
.catch(error => {
reject(error);
});
});
}, },
serveClaimByName (claimName) { serveClaimByName (claimName) {
const deferred = new Promise((resolve, reject) => { const deferred = new Promise((resolve, reject) => {
@ -56,49 +180,7 @@ module.exports = {
}); });
return deferred; return deferred;
}, },
serveClaimByClaimId (name, claimId) {
logger.debug(`serving claim "${name}" with 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
resolve(result.dataValues);
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 => {
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 => {
logger.debug('resolve returned an error');
reject(error);
});
}
})
.catch(error => reject(error));
});
return deferred;
},
serveClaimByShortUrl (name, shortUrl) { serveClaimByShortUrl (name, shortUrl) {
const deferred = new Promise((resolve, reject) => { const deferred = new Promise((resolve, reject) => {
let uri; let uri;

View file

@ -2,10 +2,11 @@ const logger = require('winston');
const licenses = ['Creative Commons', 'Public Domain', 'CC Attribution-NonCommercial 4.0 International']; const licenses = ['Creative Commons', 'Public Domain', 'CC Attribution-NonCommercial 4.0 International'];
module.exports = ({ value }) => { module.exports = ({ value }) => {
logger.debug('checking isFreePublicClaim ?');
if ((licenses.includes(value.stream.metadata.license)) && (!value.stream.metadata.fee || value.stream.metadata.fee.amount === 0)) { if ((licenses.includes(value.stream.metadata.license)) && (!value.stream.metadata.fee || value.stream.metadata.fee.amount === 0)) {
logger.debug('checking isFreePublicClaim...', true);
return true; return true;
} else { } else {
logger.debug('checking isFreePublicClaim...', false);
return false; return false;
} }
}; };

View file

@ -1,7 +1,6 @@
const logger = require('winston'); const logger = require('winston');
const db = require('../../models'); const db = require('../../models');
const lbryApi = require('./lbryApi'); const lbryApi = require('./lbryApi');
const { postToStats } = require('../controllers/statsController.js');
function determineShortUrl (claimId, claimList) { function determineShortUrl (claimId, claimList) {
logger.debug('determining short url based on claim id and claim list'); logger.debug('determining short url based on claim id and claim list');
@ -60,39 +59,39 @@ function checkLocalDbForClaims (name, shortUrl) {
}); });
} }
function getClaimAndUpdate (uri, address, height) { // function getClaimAndUpdate (uri, address, height) {
// 1. get the claim // // 1. get the claim
lbryApi // lbryApi
.getClaim(uri) // .getClaim(uri)
.then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => { // .then(({ name, claim_id, outpoint, file_name, download_path, mime_type, metadata }) => {
logger.debug(' Get returned outpoint: ', outpoint); // logger.debug(' Get returned outpoint: ', outpoint);
// 2. update the entry in db // // 2. update the entry in db
db.File // db.File
.update({ // .update({
outpoint, // outpoint,
height, // note: height is coming from the 'resolve', not 'get'. // height, // note: height is coming from the 'resolve', not 'get'.
address, // note: address is coming from the 'resolve', not 'get'. // address, // note: address is coming from the 'resolve', not 'get'.
fileName: file_name, // fileName: file_name,
filePath: download_path, // filePath: download_path,
fileType: mime_type, // fileType: mime_type,
nsfw : metadata.stream.metadata.nsfw, // nsfw : metadata.stream.metadata.nsfw,
}, { // }, {
where: { // where: {
name, // name,
claimId: claim_id, // claimId: claim_id,
}, // },
}) // })
.then(result => { // .then(result => {
logger.debug('successfully updated mysql record', result); // logger.debug('successfully updated mysql record', result);
}) // })
.catch(error => { // .catch(error => {
logger.error('sequelize error', error); // logger.error('sequelize error', error);
}); // });
}) // })
.catch(error => { // .catch(error => {
logger.error(`error while getting claim for ${uri} >> `, error); // logger.error(`error while getting claim for ${uri} >> `, error);
}); // });
} // }
module.exports = { module.exports = {
serveFile ({ fileName, fileType, filePath }, res) { serveFile ({ fileName, fileType, filePath }, res) {
@ -119,12 +118,10 @@ module.exports = {
// send the file // send the file
res.status(200).sendFile(filePath, options); res.status(200).sendFile(filePath, options);
}, },
showFile (originalUrl, ip, fileInfo, res) { showFile (fileInfo, res) {
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
res.status(200).render('show', { layout: 'show', fileInfo }); res.status(200).render('show', { layout: 'show', fileInfo });
}, },
showFileLite (originalUrl, ip, fileInfo, res) { showFileLite (fileInfo, res) {
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
res.status(200).render('showLite', { layout: 'show', fileInfo }); res.status(200).render('showLite', { layout: 'show', fileInfo });
}, },
getClaimIdByShortUrl (name, shortUrl) { getClaimIdByShortUrl (name, shortUrl) {
@ -187,38 +184,39 @@ module.exports = {
determineShortUrl (claimId, claimList) { determineShortUrl (claimId, claimList) {
return determineShortUrl(claimId, claimList); return determineShortUrl(claimId, claimList);
}, },
updateFileIfNeeded (uri, localOutpoint, localHeight) { updateFileIfNeeded (name, claimId, localOutpoint, localHeight) {
logger.debug(`Initiating resolve to check outpoint for ${uri}`); // const uri = `${name}#${claimId}`;
// 1. resolve claim // logger.debug(`Initiating resolve to check outpoint for ${uri}`);
lbryApi // // 1. resolve claim
.resolveUri(uri) // lbryApi
.then(result => { // .resolveUri(uri)
// check to make sure the result is a claim // .then(result => {
if (!result.claim) { // // check to make sure the result is a claim
logger.debug('resolve did not return a claim'); // if (!result.claim) {
return; // logger.debug('resolve did not return a claim');
} // return;
// logger.debug('resolved result:', result); // }
const resolvedOutpoint = `${result.claim.txid}:${result.claim.nout}`; // // logger.debug('resolved result:', result);
const resolvedHeight = result.claim.height; // const resolvedOutpoint = `${result.claim.txid}:${result.claim.nout}`;
const resolvedAddress = result.claim.address; // const resolvedHeight = result.claim.height;
logger.debug('database outpoint:', localOutpoint); // const resolvedAddress = result.claim.address;
logger.debug('resolved outpoint:', resolvedOutpoint); // logger.debug('database outpoint:', localOutpoint);
// 2. if the outpoint's match, no further work needed // logger.debug('resolved outpoint:', resolvedOutpoint);
if (localOutpoint === resolvedOutpoint) { // // 2. if the outpoint's match, no further work needed
logger.debug('local outpoint matched'); // if (localOutpoint === resolvedOutpoint) {
// 2. if the outpoints don't match, check the height // logger.debug('local outpoint matched');
} else if (localHeight > resolvedHeight) { // // 2. if the outpoints don't match, check the height
logger.debug('local height was greater than resolved height'); // } else if (localHeight > resolvedHeight) {
// 2. get the resolved claim // logger.debug('local height was greater than resolved height');
} else { // // 2. get the resolved claim
logger.debug(`local outpoint did not match for ${uri}. Initiating update.`); // } else {
getClaimAndUpdate(uri, resolvedAddress, resolvedHeight); // logger.debug(`local outpoint did not match for ${uri}. Initiating update.`);
} // getClaimAndUpdate(uri, resolvedAddress, resolvedHeight);
}) // }
.catch(error => { // })
logger.error(error); // .catch(error => {
}); // logger.error(error);
// });
}, },
getClaimAndHandleResponse (uri, address, height, resolve, reject) { getClaimAndHandleResponse (uri, address, height, resolve, reject) {
lbryApi lbryApi

View file

@ -1,6 +1,7 @@
const logger = require('winston');
const { serveFile, showFile, showFileLite } = require('../helpers/libraries/serveHelpers.js'); const { serveFile, showFile, showFileLite } = require('../helpers/libraries/serveHelpers.js');
const { getAssetByChannel, getAssetByShortUrl, getAssetByClaimId } = require('../controllers/serveController.js'); const { getAssetByChannel, getAssetByShortUrl, getAssetByClaimId } = require('../controllers/serveController.js');
const logger = require('winston'); const { postToStats } = require('../controllers/statsController.js');
const SERVE = 'SERVE'; const SERVE = 'SERVE';
const SHOW = 'SHOW'; const SHOW = 'SHOW';
@ -36,46 +37,61 @@ module.exports = (app) => {
channelName = identifier.substring(1); channelName = identifier.substring(1);
logger.debug('channel name =', channelName); logger.debug('channel name =', channelName);
claimType = CHANNEL; claimType = CHANNEL;
} else if (identifier.length < 40) { } else if (identifier.length === 40) {
fullClaimId = identifier; fullClaimId = identifier;
logger.debug('full claim id =', fullClaimId); logger.debug('full claim id =', fullClaimId);
claimType = CLAIMID; claimType = CLAIMID;
} else { } else if (identifier.length < 40) {
shortUrl = identifier; shortUrl = identifier;
logger.debug('short url =', shortUrl); logger.debug('short url =', shortUrl);
claimType = SHORTURL; claimType = SHORTURL;
} else {
logger.error('that url does not compute');
res.send('that url is invalid');
return;
}; };
// parse the name // parse the name
let method; let method;
let desiredExtension; let desiredExtension;
if (name.lastIndexOf('.') === -1) { if (name.indexOf('.') !== -1) {
method = SERVE; method = SERVE;
if (headers['accept'] && headers['accept'].split(',').includes('text/html')) { if (headers['accept'] && headers['accept'].split(',').includes('text/html')) {
method = SHOWLITE; method = SHOWLITE;
} }
desiredExtension = name.substring(name.indexOf('.'));
name = name.substring(0, name.indexOf('.'));
logger.debug('file extension =', desiredExtension);
} else { } else {
method = SHOW; method = SHOW;
if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) { if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) {
method = SERVE; method = SERVE;
} }
desiredExtension = name.substring(name.lastIndexOf('.'));
name = name.substring(0, name.lastIndexOf('.'));
logger.debug('file extension =', desiredExtension);
} }
logger.debug('claim name = ', name); logger.debug('claim name = ', name);
logger.debug('method =', method); logger.debug('method =', method);
getAsset(claimType, channelName, shortUrl, fullClaimId, name) getAsset(claimType, channelName, shortUrl, fullClaimId, name)
.then(result => { .then(fileInfo => {
// add file extension to the file info
fileInfo['fileExt'] = fileInfo.fileName.substring(fileInfo.fileName.lastIndexOf('.'));
// test logging
logger.debug(fileInfo);
// serve or show
if (!fileInfo) {
res.status(200).render('noClaims');
return;
}
switch (method) { switch (method) {
case SERVE: case SERVE:
serveFile(result, res); serveFile(fileInfo, res);
break; break;
case SHOWLITE: case SHOWLITE:
showFileLite(result, res); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
showFileLite(fileInfo, res);
break; break;
case SHOW: case SHOW:
showFile(result, res); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
showFile(fileInfo, res);
break; break;
default: default:
logger.error('I did not recognize that method'); logger.error('I did not recognize that method');

View file

@ -1,6 +1,6 @@
<div class="panel"> <div class="panel">
<div id="asset-placeholder"> <div id="asset-placeholder">
<a href="/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}}"> <a href="/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}">
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
<video class="show-asset" autoplay loop controls> <video class="show-asset" autoplay loop controls>
<source src="/media/{{fileInfo.fileName}}"> <source src="/media/{{fileInfo.fileName}}">

View file

@ -15,11 +15,11 @@
</div> </div>
{{!-- link to show route for asset--}} {{!-- link to show route for asset--}}
<div class="share-option"> <div class="share-option">
<a href="/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}}">Permanent Long Link</a> <a href="/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}">Permanent Long Link</a>
(fastest service) (fastest service)
<div class="input-error" id="input-error-copy-long-link" hidden="true"></div> <div class="input-error" id="input-error-copy-long-link" hidden="true"></div>
</br> </br>
<input type="text" id="long-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}}"/> <input type="text" id="long-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}"/>
<button class="copy-button" data-elementtocopy="long-link" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="long-link" onclick="copyToClipboard(event)">copy</button>
</div> </div>
{{!-- html text for embedding asset--}} {{!-- html text for embedding asset--}}
@ -28,9 +28,9 @@
<div class="input-error" id="input-error-copy-embed-text" hidden="true"></div> <div class="input-error" id="input-error-copy-embed-text" hidden="true"></div>
<br/> <br/>
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
<input type="text" id="embed-text" class="link" readonly onclick="select()" spellcheck="false" value='&lt;video width="100%" controls src="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}}"/>&lt;/video>'/> <input type="text" id="embed-text" class="link" readonly onclick="select()" spellcheck="false" value='&lt;video width="100%" controls src="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}"/>&lt;/video>'/>
{{else}} {{else}}
<input type="text" id="embed-text" class="link" readonly onclick="select()" spellcheck="false" value='&lt;img src="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}}" />'/> <input type="text" id="embed-text" class="link" readonly onclick="select()" spellcheck="false" value='&lt;img src="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}" />'/>
{{/ifConditional}} {{/ifConditional}}
<button class="copy-button" data-elementtocopy="embed-text" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="embed-text" onclick="copyToClipboard(event)">copy</button>
</div> </div>
@ -41,7 +41,7 @@
Markdown Markdown
<div class="input-error" id="input-error-copy-markdown-text" hidden="true"></div> <div class="input-error" id="input-error-copy-markdown-text" hidden="true"></div>
<br/> <br/>
<input type="text" id="markdown-text" class="link" readonly onclick="select()" spellcheck="false" value='![{{fileInfo.name}}](https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}{{fileInfo.fileExt}})'/> <input type="text" id="markdown-text" class="link" readonly onclick="select()" spellcheck="false" value='![{{fileInfo.name}}](https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}})'/>
<button class="copy-button" data-elementtocopy="markdown-text" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="markdown-text" onclick="copyToClipboard(event)">copy</button>
</div> </div>
{{/ifConditional}} {{/ifConditional}}

View file

@ -20,7 +20,12 @@
<select type="text" id="publish-license" name="license" value="license"> <select type="text" id="publish-license" name="license" value="license">
<option value="Public Domain">Public Domain</option> <option value="Public Domain">Public Domain</option>
<option value="Creative Commons">Creative Commons</option> <option value="Creative Commons">Creative Commons</option>
<option value="CC Attribution-NonCommercial 4.0 International">CC Attribution-NonCommercial 4.0 International</option> <option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
</select> </select>
<br /> <br />
<input type="checkbox" id="publish-nsfw"> <input type="checkbox" id="publish-nsfw">

View file

@ -5,7 +5,7 @@
<div class="grid" data-masonry='{ "itemSelector": ".grid-item" }'> <div class="grid" data-masonry='{ "itemSelector": ".grid-item" }'>
{{#each trendingAssets}} {{#each trendingAssets}}
{{#unless this.nsfw}} {{#unless this.nsfw}}
<a href="/{{this.name}}/{{this.claimId}}"> <a href="/{{this.claimId}}/{{this.name}}">
{{#ifConditional this.fileType '===' 'video/mp4'}} {{#ifConditional this.fileType '===' 'video/mp4'}}
<video class="grid-item trending-video" controls onloadeddata="resetTrendingLayout()"> <video class="grid-item trending-video" controls onloadeddata="resetTrendingLayout()">
<source src="/media/{{this.fileName}}" > <source src="/media/{{this.fileName}}" >

View file

@ -1,5 +1,5 @@
<div id="asset-placeholder"> <div id="asset-placeholder">
<a href="/{{fileInfo.name}}/{{fileInfo.claimId}}"> <a href="/{{fileInfo.claimId}}/{{fileInfo.name}}">
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
<video class="show-asset-lite" autoplay loop controls> <video class="show-asset-lite" autoplay loop controls>
<source src="/media/{{fileInfo.fileName}}"> <source src="/media/{{fileInfo.fileName}}">