Merge pull request #492 from lbryio/465-social-share-with-extensions

465 social share with extensions
This commit is contained in:
Bill Bittner 2018-06-27 09:51:51 -07:00 committed by GitHub
commit 536dfc9ccd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 141 additions and 80 deletions

View file

@ -101,9 +101,9 @@ var createAssetMetaTags = function createAssetMetaTags(_ref3) {
defaultThumbnail = _ref3.defaultThumbnail; defaultThumbnail = _ref3.defaultThumbnail;
var claimData = asset.claimData; var claimData = asset.claimData;
var contentType = claimData.contentType; var contentType = claimData.contentType;
var embedUrl = "".concat(siteHost, "/").concat(claimData.claimId, "/").concat(claimData.name); var videoEmbedUrl = "".concat(siteHost, "/video-embed/").concat(claimData.name, "/").concat(claimData.claimId);
var showUrl = "".concat(siteHost, "/").concat(claimData.claimId, "/").concat(claimData.name); var showUrl = "".concat(siteHost, "/").concat(claimData.claimId, "/").concat(claimData.name);
var source = "".concat(siteHost, "/").concat(claimData.claimId, "/").concat(claimData.name, ".").concat(claimData.fileExt); var source = "".concat(siteHost, "/asset/").concat(claimData.name, "/").concat(claimData.claimId);
var ogTitle = claimData.title || claimData.name; var ogTitle = claimData.title || claimData.name;
var ogDescription = claimData.description || defaultDescription; var ogDescription = claimData.description || defaultDescription;
var ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail); var ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
@ -154,7 +154,7 @@ var createAssetMetaTags = function createAssetMetaTags(_ref3) {
}); });
metaTags.push({ metaTags.push({
property: 'og:type', property: 'og:type',
content: 'video' content: 'video.other'
}); });
metaTags.push({ metaTags.push({
property: 'twitter:card', property: 'twitter:card',
@ -162,7 +162,7 @@ var createAssetMetaTags = function createAssetMetaTags(_ref3) {
}); });
metaTags.push({ metaTags.push({
property: 'twitter:player', property: 'twitter:player',
content: embedUrl content: videoEmbedUrl
}); });
metaTags.push({ metaTags.push({
property: 'twitter:player:width', property: 'twitter:player:width',

View file

@ -46,9 +46,9 @@ const createChannelMetaTags = ({siteHost, siteTitle, siteTwitter, channel}) => {
const createAssetMetaTags = ({siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail}) => { const createAssetMetaTags = ({siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail}) => {
const { claimData } = asset; const { claimData } = asset;
const { contentType } = claimData; const { contentType } = claimData;
const embedUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`; const videoEmbedUrl = `${siteHost}/video-embed/${claimData.name}/${claimData.claimId}`;
const showUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`; const showUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
const source = `${siteHost}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`; const source = `${siteHost}/asset/${claimData.name}/${claimData.claimId}`;
const ogTitle = claimData.title || claimData.name; const ogTitle = claimData.title || claimData.name;
const ogDescription = claimData.description || defaultDescription; const ogDescription = claimData.description || defaultDescription;
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail); const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
@ -68,9 +68,9 @@ const createAssetMetaTags = ({siteHost, siteTitle, siteTwitter, asset, defaultDe
metaTags.push({property: 'og:video:type', content: contentType}); metaTags.push({property: 'og:video:type', content: contentType});
metaTags.push({property: 'og:image', content: ogThumbnail}); metaTags.push({property: 'og:image', content: ogThumbnail});
metaTags.push({property: 'og:image:type', content: ogThumbnailContentType}); metaTags.push({property: 'og:image:type', content: ogThumbnailContentType});
metaTags.push({property: 'og:type', content: 'video'}); metaTags.push({property: 'og:type', content: 'video.other'});
metaTags.push({property: 'twitter:card', content: 'player'}); metaTags.push({property: 'twitter:card', content: 'player'});
metaTags.push({property: 'twitter:player', content: embedUrl}); metaTags.push({property: 'twitter:player', content: videoEmbedUrl});
metaTags.push({property: 'twitter:player:width', content: 600}); metaTags.push({property: 'twitter:player:width', content: 600});
metaTags.push({property: 'twitter:text:player_width', content: 600}); metaTags.push({property: 'twitter:text:player_width', content: 600});
metaTags.push({property: 'twitter:player:height', content: 337}); metaTags.push({property: 'twitter:player:height', content: 337});

View file

@ -0,0 +1,9 @@
const EMBED = 'EMBED';
const BROWSER = 'BROWSER';
const SOCIAL = 'SOCIAL';
module.exports = {
EMBED,
BROWSER,
SOCIAL,
};

View file

@ -0,0 +1,17 @@
const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
/*
route to serve an asset directly
*/
const serveAsset = ({ headers, ip, originalUrl, params: { claimName, claimId } }, res) => {
// send google analytics
sendGAServeEvent(headers, ip, originalUrl);
// get the claim Id and then serve the asset
getClaimIdAndServeAsset(null, null, claimName, claimId, originalUrl, ip, res);
};
module.exports = serveAsset;

View file

@ -3,11 +3,10 @@ const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../utils/lbryUri.js'); const lbryUri = require('../utils/lbryUri.js');
const determineResponseType = require('../utils/determineResponseType.js'); const determineRequestType = require('../utils/determineRequestType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js'); const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
const logRequestData = require('../utils/logRequestData.js');
const SERVE = 'SERVE'; const { EMBED } = require('../constants/request_types.js');
/* /*
@ -15,7 +14,7 @@ const SERVE = 'SERVE';
*/ */
const serverAssetByClaim = (req, res) => { const serveByClaim = (req, res) => {
const { headers, ip, originalUrl, params } = req; const { headers, ip, originalUrl, params } = req;
// decide if this is a show request // decide if this is a show request
let hasFileExtension; let hasFileExtension;
@ -24,13 +23,11 @@ const serverAssetByClaim = (req, res) => {
} catch (error) { } catch (error) {
return res.status(400).json({success: false, message: error.message}); return res.status(400).json({success: false, message: error.message});
} }
let responseType = determineResponseType(hasFileExtension, headers); // determine request type
if (responseType !== SERVE) { let requestType = determineRequestType(hasFileExtension, headers);
if (requestType !== EMBED) {
return handleShowRender(req, res); return handleShowRender(req, res);
} }
// handle serve request
// send google analytics
sendGAServeEvent(headers, ip, originalUrl);
// parse the claim // parse the claim
let claimName; let claimName;
try { try {
@ -38,10 +35,10 @@ const serverAssetByClaim = (req, res) => {
} catch (error) { } catch (error) {
return res.status(400).json({success: false, message: error.message}); return res.status(400).json({success: false, message: error.message});
} }
// log the request data for debugging // send google analytics
logRequestData(responseType, claimName, null, null); sendGAServeEvent(headers, ip, originalUrl);
// get the claim Id and then serve the asset // get the claim Id and then serve the asset
getClaimIdAndServeAsset(null, null, claimName, null, originalUrl, ip, res); getClaimIdAndServeAsset(null, null, claimName, null, originalUrl, ip, res);
}; };
module.exports = serverAssetByClaim; module.exports = serveByClaim;

View file

@ -3,12 +3,11 @@ const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../utils/lbryUri.js'); const lbryUri = require('../utils/lbryUri.js');
const determineResponseType = require('../utils/determineResponseType.js'); const determineRequestType = require('../utils/determineRequestType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js'); const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
const flipClaimNameAndId = require('../utils/flipClaimNameAndId.js'); const flipClaimNameAndId = require('../utils/flipClaimNameAndId.js');
const logRequestData = require('../utils/logRequestData.js');
const SERVE = 'SERVE'; const { EMBED } = require('../constants/request_types.js');
/* /*
@ -16,22 +15,20 @@ const SERVE = 'SERVE';
*/ */
const serverAssetByIdentifierAndClaim = (req, res) => { const serverByIdentifierAndClaim = (req, res) => {
const { headers, ip, originalUrl, params } = req; const { headers, ip, originalUrl, params } = req;
// decide if this is a show request // parse request
let hasFileExtension; let hasFileExtension;
try { try {
({ hasFileExtension } = lbryUri.parseModifier(params.claim)); ({ hasFileExtension } = lbryUri.parseModifier(params.claim));
} catch (error) { } catch (error) {
return res.status(400).json({success: false, message: error.message}); return res.status(400).json({success: false, message: error.message});
} }
let responseType = determineResponseType(hasFileExtension, headers); // determine request type
if (responseType !== SERVE) { let requestType = determineRequestType(hasFileExtension, headers);
if (requestType !== EMBED) {
return handleShowRender(req, res); return handleShowRender(req, res);
} }
// handle serve request
// send google analytics
sendGAServeEvent(headers, ip, originalUrl);
// parse the claim // parse the claim
let claimName; let claimName;
try { try {
@ -50,10 +47,10 @@ const serverAssetByIdentifierAndClaim = (req, res) => {
if (!isChannel) { if (!isChannel) {
[claimId, claimName] = flipClaimNameAndId(claimId, claimName); [claimId, claimName] = flipClaimNameAndId(claimId, claimName);
} }
// log the request data for debugging // send google analytics
logRequestData(responseType, claimName, channelName, claimId); sendGAServeEvent(headers, ip, originalUrl);
// get the claim Id and then serve the asset // get the claim Id and then serve the asset
getClaimIdAndServeAsset(channelName, channelClaimId, claimName, claimId, originalUrl, ip, res); getClaimIdAndServeAsset(channelName, channelClaimId, claimName, claimId, originalUrl, ip, res);
}; };
module.exports = serverAssetByIdentifierAndClaim; module.exports = serverByIdentifierAndClaim;

View file

@ -0,0 +1,54 @@
const logger = require('winston');
const { EMBED, BROWSER, SOCIAL } = require('../constants/request_types.js');
function headersMatchesSocialBotList (headers) {
const userAgent = headers['user-agent'];
const socialBotList = {
'facebookexternalhit': 1,
'Twitterbot' : 1,
};
return socialBotList[userAgent];
}
function clientAcceptsHtml ({accept}) {
return accept && accept.match(/text\/html/);
};
function requestIsFromBrowser (headers) {
return headers['user-agent'] && headers['user-agent'].match(/Mozilla/);
};
function clientWantsAsset ({accept, range}) {
const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/) && !accept.match(/text\/\*/);
const videoIsWanted = accept && range;
return imageIsWanted || videoIsWanted;
};
const determineRequestType = (hasFileExtension, headers) => {
let responseType;
logger.debug('headers:', headers);
// return early with 'show' if headers match the list
if (headersMatchesSocialBotList(headers)) {
return SOCIAL;
}
// if request is not from a social bot...
if (hasFileExtension) {
// assume embed,
responseType = EMBED;
// but change to browser if client accepts html.
if (clientAcceptsHtml(headers)) {
responseType = BROWSER;
}
// if request does not have file extentsion...
} else {
// assume browser,
responseType = BROWSER;
// but change to embed if someone embeded a show url...
if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) {
responseType = EMBED;
}
}
return responseType;
};
module.exports = determineRequestType;

View file

@ -1,37 +0,0 @@
const logger = require('winston');
const SERVE = 'SERVE';
const SHOW = 'SHOW';
function clientAcceptsHtml ({accept}) {
return accept && accept.match(/text\/html/);
};
function requestIsFromBrowser (headers) {
return headers['user-agent'] && headers['user-agent'].match(/Mozilla/);
};
function clientWantsAsset ({accept, range}) {
const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/) && !accept.match(/text\/\*/);
const videoIsWanted = accept && range;
return imageIsWanted || videoIsWanted;
};
const determineResponseType = (hasFileExtension, headers) => {
let responseType;
if (hasFileExtension) {
responseType = SERVE; // assume a serve request if file extension is present
if (clientAcceptsHtml(headers)) { // if the request comes from a browser, change it to a show request
responseType = SHOW;
}
} else {
responseType = SHOW;
if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url
logger.debug('Show request came from browser but wants an image/video. Changing response to serve...');
responseType = SERVE;
}
}
return responseType;
};
module.exports = determineResponseType;

View file

@ -17,6 +17,7 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId
getClaimId(channelName, channelClaimId, claimName, claimId) getClaimId(channelName, channelClaimId, claimName, claimId)
.then(fullClaimId => { .then(fullClaimId => {
claimId = fullClaimId; claimId = fullClaimId;
logger.debug('FULL CLAIM ID:', fullClaimId);
return db.Blocked.isNotBlocked(fullClaimId, claimName); return db.Blocked.isNotBlocked(fullClaimId, claimName);
}) })
.then(() => { .then(() => {

View file

@ -1,10 +1,10 @@
const { details: { host } } = require('@config/siteConfig'); const { details: { host } } = require('@config/siteConfig');
const sendEmbedPage = ({ params }, res) => { const sendVideoEmbedPage = ({ params }, res) => {
const claimId = params.claimId; const claimId = params.claimId;
const name = params.name; const name = params.name;
// get and render the content // get and render the content
res.status(200).render('embed', { layout: 'embed', host, claimId, name }); res.status(200).render('embed', { layout: 'embed', host, claimId, name });
}; };
module.exports = sendEmbedPage; module.exports = sendVideoEmbedPage;

View file

@ -29,7 +29,7 @@ module.exports = (app) => {
app.get('/api/claim/data/:claimName/:claimId', claimData); app.get('/api/claim/data/:claimName/:claimId', claimData);
app.get('/api/claim/get/:name/:claimId', claimGet); app.get('/api/claim/get/:name/:claimId', claimGet);
app.get('/api/claim/list/:name', claimList); app.get('/api/claim/list/:name', claimList);
app.post('/api/claim/long-id', claimLongId); app.post('/api/claim/long-id', claimLongId); // should be a get
app.post('/api/claim/publish', multipartMiddleware, claimPublish); app.post('/api/claim/publish', multipartMiddleware, claimPublish);
app.get('/api/claim/resolve/:name/:claimId', claimResolve); app.get('/api/claim/resolve/:name/:claimId', claimResolve);
app.get('/api/claim/short-id/:longId/:name', claimShortId); app.get('/api/claim/short-id/:longId/:name', claimShortId);

View file

@ -1,7 +1,9 @@
const serveAssetByClaim = require('../../controllers/assets/serveByClaim'); const serveByClaim = require('../../controllers/assets/serveByClaim');
const serveAssetByIdentifierAndClaim = require('../../controllers/assets/serveByIdentifierAndClaim'); const serveByIdentifierAndClaim = require('../../controllers/assets/serveByIdentifierAndClaim');
const serveAsset = require('../../controllers/assets/serveAsset');
module.exports = (app, db) => { module.exports = (app) => {
app.get('/:identifier/:claim', serveAssetByIdentifierAndClaim); app.get('/asset/:claimName/:claimId/', serveAsset);
app.get('/:claim', serveAssetByClaim); app.get('/:identifier/:claim', serveByIdentifierAndClaim);
app.get('/:claim', serveByClaim);
}; };

View file

@ -1,5 +1,5 @@
const handlePageRequest = require('../../controllers/pages/sendReactApp'); const handlePageRequest = require('../../controllers/pages/sendReactApp');
const handleEmbedRequest = require('../../controllers/pages/sendEmbedPage'); const handleVideoEmbedRequest = require('../../controllers/pages/sendVideoEmbedPage');
const redirect = require('../../controllers/utils/redirect'); const redirect = require('../../controllers/utils/redirect');
module.exports = (app) => { module.exports = (app) => {
@ -10,5 +10,5 @@ module.exports = (app) => {
app.get('/popular', handlePageRequest); app.get('/popular', handlePageRequest);
app.get('/new', handlePageRequest); app.get('/new', handlePageRequest);
app.get('/multisite', handlePageRequest); app.get('/multisite', handlePageRequest);
app.get('/embed/:claimId/:name', handleEmbedRequest); // route to send embedable video player (for twitter) app.get('/video-embed/:name/:claimId', handleVideoEmbedRequest); // for twitter
}; };

21
test/test.html Normal file
View file

@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<img src="https://media.giphy.com/media/vwEHGjx71HSytx5mY8/giphy-facebook_s.jpg" alt="test embed"/>
<p>no identifier, no ending</p>
<img src="https://dev1.spee.ch/typingcat" alt="no identifier, no ending"/>
<p>no identifier, yes ending</p>
<img src="https://dev1.spee.ch/typingcat.gif" alt="no identifier, yes ending"/>
<p>yes identifier, no ending</p>
<img src="https://dev1.spee.ch/8/typingcat" alt="yes identifier, no ending"/>
<p>yes identifier, yes ending</p>
<img src="https://dev1.spee.ch/8/typingcat.gif" alt="yes identifier, yes ending"/>
</body>
</html>