serve static assets on urls with file extension #538

Merged
bones7242 merged 19 commits from 515-showlite-serves-asset into master 2018-07-20 18:57:12 +02:00
38 changed files with 779 additions and 612 deletions

View file

@ -19,10 +19,10 @@ var AssetPreview = function AssetPreview(_ref) {
fileExt = _ref$claimData.fileExt, fileExt = _ref$claimData.fileExt,
contentType = _ref$claimData.contentType, contentType = _ref$claimData.contentType,
thumbnail = _ref$claimData.thumbnail; thumbnail = _ref$claimData.thumbnail;
var directSourceLink = "asset/".concat(name, "/").concat(claimId); var embedUrl = "/".concat(claimId, "/").concat(name, ".").concat(fileExt);
var showUrlLink = "/".concat(claimId, "/").concat(name); var showUrl = "/".concat(claimId, "/").concat(name);
return _react.default.createElement(_reactRouterDom.Link, { return _react.default.createElement(_reactRouterDom.Link, {
to: showUrlLink to: showUrl
}, function () { }, function () {
switch (contentType) { switch (contentType) {
case 'image/jpeg': case 'image/jpeg':
@ -31,7 +31,7 @@ var AssetPreview = function AssetPreview(_ref) {
case 'image/gif': case 'image/gif':
return _react.default.createElement("img", { return _react.default.createElement("img", {
className: 'asset-preview-image', className: 'asset-preview-image',
src: directSourceLink, src: embedUrl,
alt: name alt: name
}); });

View file

@ -60,6 +60,7 @@ function (_React$Component) {
contentType = _this$props$asset$cla2.contentType, contentType = _this$props$asset$cla2.contentType,
fileExt = _this$props$asset$cla2.fileExt, fileExt = _this$props$asset$cla2.fileExt,
thumbnail = _this$props$asset$cla2.thumbnail; thumbnail = _this$props$asset$cla2.thumbnail;
var sourceUrl = "/".concat(claimId, "/").concat(name, ".").concat(fileExt);
return _react.default.createElement("div", { return _react.default.createElement("div", {
className: 'asset-display' className: 'asset-display'
}, status === _asset_display_states.LOCAL_CHECK && _react.default.createElement("div", null, _react.default.createElement("p", null, "Checking to see if Spee.ch has your asset locally...")), status === _asset_display_states.UNAVAILABLE && _react.default.createElement("div", null, _react.default.createElement("p", null, "Sit tight, we're searching the LBRY blockchain for your asset!"), _react.default.createElement(_ProgressBar.default, { }, status === _asset_display_states.LOCAL_CHECK && _react.default.createElement("div", null, _react.default.createElement("p", null, "Checking to see if Spee.ch has your asset locally...")), status === _asset_display_states.UNAVAILABLE && _react.default.createElement("div", null, _react.default.createElement("p", null, "Sit tight, we're searching the LBRY blockchain for your asset!"), _react.default.createElement(_ProgressBar.default, {
@ -82,7 +83,7 @@ function (_React$Component) {
case 'image/gif': case 'image/gif':
return _react.default.createElement("img", { return _react.default.createElement("img", {
className: "asset-image", className: "asset-image",
src: "/asset/".concat(name, "/").concat(claimId), src: sourceUrl,
alt: name alt: name
}); });
@ -92,11 +93,11 @@ function (_React$Component) {
controls: true, controls: true,
poster: thumbnail poster: thumbnail
}, _react.default.createElement("source", { }, _react.default.createElement("source", {
src: "/asset/".concat(name, "/").concat(claimId) src: sourceUrl
}), _react.default.createElement("p", null, "Your browser does not support the ", _react.default.createElement("code", null, "video"), " element.")); }), _react.default.createElement("p", null, "Your browser does not support the ", _react.default.createElement("code", null, "video"), " element."));
default: default:
return _react.default.createElement("p", null, "Unsupported file type"); return _react.default.createElement("p", null, "Unsupported content type");
} }
}()); }());
} }

View file

@ -89,7 +89,7 @@ function (_React$Component) {
}), }),
content: _react.default.createElement(_ClickToCopy.default, { content: _react.default.createElement(_ClickToCopy.default, {
id: 'short-link', id: 'short-link',
value: "".concat(host, "/").concat(shortId, "/").concat(name, ".").concat(fileExt) value: "".concat(host, "/").concat(shortId, "/").concat(name)
}) })
})), _react.default.createElement(_Row.default, null, _react.default.createElement(_RowLabeled.default, { })), _react.default.createElement(_Row.default, null, _react.default.createElement(_RowLabeled.default, {
label: _react.default.createElement(_Label.default, { label: _react.default.createElement(_Label.default, {
@ -102,9 +102,9 @@ function (_React$Component) {
id: 'embed-text-image', id: 'embed-text-image',
value: "<img src=\"".concat(host, "/").concat(claimId, "/").concat(name, ".").concat(fileExt, "\"/>") value: "<img src=\"".concat(host, "/").concat(claimId, "/").concat(name, ".").concat(fileExt, "\"/>")
})) }))
})), _react.default.createElement(_Row.default, null, _react.default.createElement(_SpaceBetween.default, null, _react.default.createElement(_reactRouterDom.Link, { })), _react.default.createElement(_Row.default, null, _react.default.createElement(_SpaceBetween.default, null, _react.default.createElement("a", {
className: "link--primary", className: "link--primary",
to: "/".concat(shortId, "/").concat(name, ".").concat(fileExt) href: "".concat(host, "/").concat(claimId, "/").concat(name, ".").concat(fileExt)
}, "Direct Link"), _react.default.createElement("a", { }, "Direct Link"), _react.default.createElement("a", {
className: 'link--primary', className: 'link--primary',
href: "".concat(host, "/").concat(claimId, "/").concat(name, ".").concat(fileExt), href: "".concat(host, "/").concat(claimId, "/").concat(name, ".").concat(fileExt),

View file

@ -11,24 +11,6 @@ var _view = _interopRequireDefault(require("./view"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var mapStateToProps = function mapStateToProps(_ref) { var _default = (0, _reactRedux.connect)(null, null)(_view.default);
var site = _ref.site;
var defaultDescription = site.defaultDescription,
defaultThumbnail = site.defaultThumbnail,
siteDescription = site.description,
siteHost = site.host,
siteTitle = site.title,
siteTwitter = site.twitter;
return {
defaultDescription: defaultDescription,
defaultThumbnail: defaultThumbnail,
siteDescription: siteDescription,
siteHost: siteHost,
siteTitle: siteTitle,
siteTwitter: siteTwitter
};
};
var _default = (0, _reactRedux.connect)(mapStateToProps, null)(_view.default);
exports.default = _default; exports.default = _default;

View file

@ -11,11 +11,11 @@ var _reactHelmet = _interopRequireDefault(require("react-helmet"));
var _propTypes = _interopRequireDefault(require("prop-types")); var _propTypes = _interopRequireDefault(require("prop-types"));
var _pageTitle = require("../../utils/pageTitle"); var _createPageTitle = _interopRequireDefault(require("../../utils/createPageTitle"));
var _metaTags = require("../../utils/metaTags"); var _createMetaTags = _interopRequireDefault(require("../../utils/createMetaTags"));
var _canonicalLink = require("../../utils/canonicalLink"); var _createCanonicalLink = _interopRequireDefault(require("../../utils/createCanonicalLink"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@ -49,33 +49,19 @@ function (_React$Component) {
_createClass(SEO, [{ _createClass(SEO, [{
key: "render", key: "render",
value: function render() { value: function render() {
// props from state // props from parent
var _this$props = this.props, var _this$props = this.props,
defaultDescription = _this$props.defaultDescription, asset = _this$props.asset,
defaultThumbnail = _this$props.defaultThumbnail, channel = _this$props.channel,
siteDescription = _this$props.siteDescription, pageUri = _this$props.pageUri;
siteHost = _this$props.siteHost,
siteTitle = _this$props.siteTitle,
siteTwitter = _this$props.siteTwitter; // props from parent
var _this$props2 = this.props,
asset = _this$props2.asset,
channel = _this$props2.channel,
pageUri = _this$props2.pageUri;
var pageTitle = this.props.pageTitle; // create page title, tags, and canonical link var pageTitle = this.props.pageTitle; // create page title, tags, and canonical link
pageTitle = (0, _pageTitle.createPageTitle)(siteTitle, pageTitle); pageTitle = (0, _createPageTitle.default)(pageTitle);
var metaTags = (0, _metaTags.createMetaTags)({ var metaTags = (0, _createMetaTags.default)({
siteDescription: siteDescription,
siteHost: siteHost,
siteTitle: siteTitle,
siteTwitter: siteTwitter,
asset: asset, asset: asset,
channel: channel, channel: channel
defaultDescription: defaultDescription,
defaultThumbnail: defaultThumbnail
}); });
var canonicalLink = (0, _canonicalLink.createCanonicalLink)(asset, channel, pageUri, siteHost); // render results var canonicalLink = (0, _createCanonicalLink.default)(asset, channel, pageUri); // render results
return _react.default.createElement(_reactHelmet.default, { return _react.default.createElement(_reactHelmet.default, {
title: pageTitle, title: pageTitle,

View file

@ -0,0 +1,105 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _siteConfig = _interopRequireDefault(require("@config/siteConfig.json"));
var _determineContentTypeFromExtension = _interopRequireDefault(require("./determineContentTypeFromExtension"));
var _createMetaTagsArray = _interopRequireDefault(require("./createMetaTagsArray"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _siteConfig$details = _siteConfig.default.details,
host = _siteConfig$details.host,
siteTitle = _siteConfig$details.title,
twitter = _siteConfig$details.twitter,
_siteConfig$assetDefa = _siteConfig.default.assetDefaults,
defaultDescription = _siteConfig$assetDefa.description,
defaultThumbnail = _siteConfig$assetDefa.thumbnail;
var VIDEO = 'VIDEO';
var IMAGE = 'IMAGE';
var GIF = 'GIF';
var determineMediaType = function determineMediaType(contentType) {
switch (contentType) {
case 'image/jpg':
case 'image/jpeg':
case 'image/png':
return IMAGE;
case 'image/gif':
return GIF;
case 'video/mp4':
case 'video/webm':
return VIDEO;
default:
return '';
}
};
var createAssetMetaTags = function createAssetMetaTags(asset) {
var claimData = asset.claimData;
var contentType = claimData.contentType;
var showUrl = "".concat(host, "/").concat(claimData.claimId, "/").concat(claimData.name);
var serveUrl = "".concat(host, "/").concat(claimData.claimId, "/").concat(claimData.name, ".").concat(claimData.fileExt);
var ogTitle = claimData.title || claimData.name;
var ogDescription = claimData.description || defaultDescription;
var ogThumbnailContentType = (0, _determineContentTypeFromExtension.default)(claimData.thumbnail);
var ogThumbnail = claimData.thumbnail || defaultThumbnail; // {property: 'og:title'] = ogTitle},
var metaTags = {
'og:title': ogTitle,
'twitter:title': ogTitle,
'og:description': ogDescription,
'twitter:description': ogDescription,
'og:url': showUrl,
'og:site_name': siteTitle,
'twitter:site': twitter,
'fb:app_id': '1371961932852223'
};
if (determineMediaType(contentType) === VIDEO) {
var videoEmbedUrl = "".concat(host, "/video-embed/").concat(claimData.name, "/").concat(claimData.claimId); // card type tags
metaTags['og:type'] = 'video.other';
metaTags['twitter:card'] = 'player';
metaTags['twitter:player'] = videoEmbedUrl;
metaTags['twitter:player:width'] = 600;
metaTags['twitter:text:player_width'] = 600;
metaTags['twitter:player:height'] = 350;
metaTags['twitter:player:stream'] = serveUrl;
metaTags['twitter:player:stream:content_type'] = contentType; // video tags
metaTags['og:video'] = serveUrl;
metaTags['og:video:secure_url'] = serveUrl;
metaTags['og:video:type'] = contentType; // image tags
metaTags['og:image'] = ogThumbnail;
metaTags['og:image:width'] = 600;
metaTags['og:image:height'] = 315;
metaTags['og:image:type'] = ogThumbnailContentType;
metaTags['twitter:image'] = ogThumbnail;
} else {
// card type tags
metaTags['og:type'] = 'article';
metaTags['twitter:card'] = 'summary_large_image'; // image tags
metaTags['og:image'] = serveUrl;
metaTags['og:image'] = serveUrl;
metaTags['og:image:width'] = 600;
metaTags['og:image:height'] = 315;
metaTags['og:image:type'] = contentType;
metaTags['twitter:image'] = serveUrl;
}
return (0, _createMetaTagsArray.default)(metaTags);
};
var _default = createAssetMetaTags;
exports.default = _default;

View file

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _siteConfig = _interopRequireDefault(require("@config/siteConfig.json"));
var _determineContentTypeFromExtension = _interopRequireDefault(require("./determineContentTypeFromExtension.js"));
var _createMetaTagsArray = _interopRequireDefault(require("./createMetaTagsArray"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _siteConfig$details = _siteConfig.default.details,
description = _siteConfig$details.description,
host = _siteConfig$details.host,
title = _siteConfig$details.title,
twitter = _siteConfig$details.twitter,
thumbnail = _siteConfig.default.assetDefaults.thumbnail;
var createBasicMetaTags = function createBasicMetaTags() {
var metaTags = {
// page details
'og:title': title,
'twitter:title': title,
'og:description': description,
'twitter:description': description,
// url
'og:url': host,
// site id
'og:site_name': title,
'twitter:site': twitter,
'fb:app_id': '1371961932852223',
// card type
'og:type': 'article',
'twitter:card': 'summary_large_image',
// image
'og:image': thumbnail,
'og:image:width': 600,
'og:image:height': 315,
'og:image:type': (0, _determineContentTypeFromExtension.default)(thumbnail),
'twitter:image': thumbnail,
'twitter:image:alt': 'Spee.ch Logo'
};
return (0, _createMetaTagsArray.default)(metaTags);
};
var _default = createBasicMetaTags;
exports.default = _default;

View file

@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _siteConfig = _interopRequireDefault(require("@config/siteConfig.json"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var host = _siteConfig.default.details.host;
var createBasicCanonicalLink = function createBasicCanonicalLink(page) {
return "".concat(host, "/").concat(page);
};
var createAssetCanonicalLink = function createAssetCanonicalLink(asset) {
var channelName, certificateId, name, claimId;
if (asset.claimData) {
var _asset$claimData = asset.claimData;
channelName = _asset$claimData.channelName;
certificateId = _asset$claimData.certificateId;
name = _asset$claimData.name;
claimId = _asset$claimData.claimId;
}
if (channelName) {
return "".concat(host, "/").concat(channelName, ":").concat(certificateId, "/").concat(name);
skhameneh commented 2018-07-20 16:04:16 +02:00 (Migrated from github.com)
Review

use template strings?
e.g.

return `${host}/${channelName}:${certificateId}/${name}`
use template strings? e.g. ``` return `${host}/${channelName}:${certificateId}/${name}` ```
bones7242 commented 2018-07-20 18:56:33 +02:00 (Migrated from github.com)
Review

This is the babel transpiled code :)

This is the babel transpiled code :)
bones7242 commented 2018-07-20 18:57:07 +02:00 (Migrated from github.com)
Review

once I merge the www.spee.ch and spee.ch repos we should be able to keep this out of version control

once I merge the www.spee.ch and spee.ch repos we should be able to keep this out of version control
}
return "".concat(host, "/").concat(claimId, "/").concat(name);
};
var createChannelCanonicalLink = function createChannelCanonicalLink(channel) {
var name = channel.name,
longId = channel.longId;
return "".concat(host, "/").concat(name, ":").concat(longId);
};
var createCanonicalLink = function createCanonicalLink(asset, channel, page) {
if (asset) {
return createAssetCanonicalLink(asset);
}
if (channel) {
return createChannelCanonicalLink(channel);
}
return createBasicCanonicalLink(page);
};
var _default = createCanonicalLink;
exports.default = _default;

View file

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.createChannelMetaTags = void 0;
var _siteConfig = _interopRequireDefault(require("@config/siteConfig.json"));
var _determineContentTypeFromExtension = _interopRequireDefault(require("./determineContentTypeFromExtension"));
var _createMetaTagsArray = _interopRequireDefault(require("./createMetaTagsArray"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _siteConfig$details = _siteConfig.default.details,
host = _siteConfig$details.host,
siteTitle = _siteConfig$details.title,
twitter = _siteConfig$details.twitter,
defaultThumbnail = _siteConfig.default.assetDefaults.thumbnail;
var createChannelMetaTags = function createChannelMetaTags(channel) {
var name = channel.name,
longId = channel.longId;
var metaTags = {
// page detail tags
'og:title': "".concat(name, " on ").concat(siteTitle),
'twitter:title': "".concat(name, " on ").concat(siteTitle),
'og:description': "".concat(name, ", a channel on ").concat(siteTitle),
'twitter:description': "".concat(name, ", a channel on ").concat(siteTitle),
// url
'og:url': "".concat(host, "/").concat(name, ":").concat(longId),
// site info
'og:site_name': siteTitle,
'twitter:site': twitter,
'fb:app_id': '1371961932852223',
// card type tags
'og:type': 'article',
'twitter:card': 'summary_large_image',
// image tags
'og:image': defaultThumbnail,
'og:image:width': 600,
'og:image:height': 315,
'og:image:type': (0, _determineContentTypeFromExtension.default)(defaultThumbnail),
'twitter:image': defaultThumbnail,
'twitter:image:alt': 'Spee.ch Logo'
};
return (0, _createMetaTagsArray.default)(metaTags);
};
exports.createChannelMetaTags = createChannelMetaTags;
var _default = createChannelMetaTags;
exports.default = _default;

View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _createAssetMetaTags = _interopRequireDefault(require("./createAssetMetaTags"));
var _createChannelMetaTags = _interopRequireDefault(require("./createChannelMetaTags.js"));
var _createBasicMetaTags = _interopRequireDefault(require("./createBasicMetaTags.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var createMetaTags = function createMetaTags(_ref) {
var asset = _ref.asset,
channel = _ref.channel;
if (asset) {
return (0, _createAssetMetaTags.default)(asset);
}
if (channel) {
return (0, _createChannelMetaTags.default)(channel);
}
return (0, _createBasicMetaTags.default)();
};
var _default = createMetaTags;
exports.default = _default;

View file

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var createMetaTagsArray = function createMetaTagsArray(metaTagsObject) {
var metaTagsArray = [];
for (var key in metaTagsObject) {
if (metaTagsObject.hasOwnProperty(key)) {
metaTagsArray.push({
property: key,
content: metaTagsObject[key]
});
}
}
return metaTagsArray;
};
var _default = createMetaTagsArray;
exports.default = _default;

View file

@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _siteConfig = _interopRequireDefault(require("@config/siteConfig.json"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var siteTitle = _siteConfig.default.details.title;
var createPageTitle = function createPageTitle(pageTitle) {
if (!pageTitle) {
return "".concat(siteTitle);
}
return "".concat(siteTitle, " - ").concat(pageTitle);
};
var _default = createPageTitle;
exports.default = _default;

View file

@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var determineContentTypeFromExtension = function determineContentTypeFromExtension(thumbnail) {
if (thumbnail) {
var fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
switch (fileExt) {
case 'jpeg':
case 'jpg':
return 'image/jpg';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
case 'mp4':
return 'video/mp4';
default:
return '';
}
}
return '';
};
var _default = determineContentTypeFromExtension;
exports.default = _default;

View file

@ -5,270 +5,25 @@ Object.defineProperty(exports, "__esModule", {
}); });
exports.createMetaTags = void 0; exports.createMetaTags = void 0;
var determineOgThumbnailContentType = function determineOgThumbnailContentType(thumbnail) { var createAssetMetaTags = require('createAssetMetaTags.js');
if (thumbnail) {
var fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
switch (fileExt) { var createChannelMetaTags = require('createChannelMetaTags.js');
case 'jpeg':
case 'jpg':
return 'image/jpeg';
case 'png': var createBasicMetaTags = require('createBasicMetaTags.js');
return 'image/png';
case 'gif': var createMetaTags = function createMetaTags(_ref) {
return 'image/gif'; var asset = _ref.asset,
channel = _ref.channel;
case 'mp4':
return 'video/mp4';
default:
return 'image/jpeg';
}
}
return '';
};
var createBasicMetaTags = function createBasicMetaTags(_ref) {
var siteHost = _ref.siteHost,
siteDescription = _ref.siteDescription,
siteTitle = _ref.siteTitle,
siteTwitter = _ref.siteTwitter,
defaultThumbnail = _ref.defaultThumbnail;
return [{
property: 'og:title',
content: siteTitle
}, {
property: 'twitter:title',
content: siteTitle
}, {
property: 'og:url',
content: siteHost
}, {
property: 'og:site_name',
content: siteTitle
}, {
property: 'og:description',
content: siteDescription
}, {
property: 'twitter:description',
content: siteDescription
}, {
property: 'twitter:site',
content: siteTwitter
}, {
property: 'twitter:card',
content: 'summary_large_image'
}, {
property: 'og:image',
content: defaultThumbnail
}, {
property: 'twitter:image',
content: defaultThumbnail
}, {
property: 'og:image:type',
content: 'image/jpeg'
}];
};
var createChannelMetaTags = function createChannelMetaTags(_ref2) {
var siteHost = _ref2.siteHost,
siteTitle = _ref2.siteTitle,
siteTwitter = _ref2.siteTwitter,
channel = _ref2.channel;
var name = channel.name,
longId = channel.longId;
return [{
property: 'og:title',
content: "".concat(name, " on ").concat(siteTitle)
}, {
property: 'twitter:title',
content: "".concat(name, " on ").concat(siteTitle)
}, {
property: 'og:url',
content: "".concat(siteHost, "/").concat(name, ":").concat(longId)
}, {
property: 'og:site_name',
content: siteTitle
}, {
property: 'og:description',
content: "".concat(name, ", a channel on ").concat(siteTitle)
}, {
property: 'twitter:site',
content: siteTwitter
}, {
property: 'twitter:card',
content: 'summary'
}];
};
var createAssetMetaTags = function createAssetMetaTags(_ref3) {
var siteHost = _ref3.siteHost,
siteTitle = _ref3.siteTitle,
siteTwitter = _ref3.siteTwitter,
asset = _ref3.asset,
defaultDescription = _ref3.defaultDescription,
defaultThumbnail = _ref3.defaultThumbnail;
var claimData = asset.claimData;
var contentType = claimData.contentType;
var videoEmbedUrl = "".concat(siteHost, "/video-embed/").concat(claimData.name, "/").concat(claimData.claimId);
var showUrl = "".concat(siteHost, "/").concat(claimData.claimId, "/").concat(claimData.name);
var source = "".concat(siteHost, "/asset/").concat(claimData.name, "/").concat(claimData.claimId);
var ogTitle = claimData.title || claimData.name;
var ogDescription = claimData.description || defaultDescription;
var ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
var ogThumbnail = claimData.thumbnail || defaultThumbnail;
var metaTags = [{
property: 'og:title',
content: ogTitle
}, {
property: 'twitter:title',
content: ogTitle
}, {
property: 'og:url',
content: showUrl
}, {
property: 'og:site_name',
content: siteTitle
}, {
property: 'og:description',
content: ogDescription
}, {
property: 'twitter:description',
content: ogDescription
}, {
property: 'og:image:width',
content: 600
}, {
property: 'og:image:height',
content: 315
}, {
property: 'twitter:site',
content: siteTwitter
}];
if (contentType === 'video/mp4' || contentType === 'video/webm') {
metaTags.push({
property: 'og:video',
content: source
});
metaTags.push({
property: 'og:video:secure_url',
content: source
});
metaTags.push({
property: 'og:video:type',
content: contentType
});
metaTags.push({
property: 'og:image',
content: ogThumbnail
});
metaTags.push({
property: 'twitter:image',
content: ogThumbnail
});
metaTags.push({
property: 'og:image:type',
content: ogThumbnailContentType
});
metaTags.push({
property: 'og:type',
content: 'video.other'
});
metaTags.push({
property: 'twitter:card',
content: 'player'
});
metaTags.push({
property: 'twitter:player',
content: videoEmbedUrl
});
metaTags.push({
property: 'twitter: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:stream',
content: source
});
metaTags.push({
property: 'twitter:player:stream:content_type',
content: contentType
});
} else {
metaTags.push({
property: 'og:image',
content: source
});
metaTags.push({
property: 'twitter:image',
content: source
});
metaTags.push({
property: 'og:image:type',
content: contentType
});
metaTags.push({
property: 'og:type',
content: 'article'
});
metaTags.push({
property: 'twitter:card',
content: 'summary_large_image'
});
}
return metaTags;
};
var createMetaTags = function createMetaTags(_ref4) {
var siteDescription = _ref4.siteDescription,
siteHost = _ref4.siteHost,
siteTitle = _ref4.siteTitle,
siteTwitter = _ref4.siteTwitter,
asset = _ref4.asset,
channel = _ref4.channel,
defaultDescription = _ref4.defaultDescription,
defaultThumbnail = _ref4.defaultThumbnail;
if (asset) { if (asset) {
return createAssetMetaTags({ return createAssetMetaTags(asset);
siteHost: siteHost,
siteTitle: siteTitle,
siteTwitter: siteTwitter,
asset: asset,
defaultDescription: defaultDescription,
defaultThumbnail: defaultThumbnail
});
} }
if (channel) { if (channel) {
return createChannelMetaTags({ return createChannelMetaTags(channel);
siteHost: siteHost,
siteTitle: siteTitle,
siteTwitter: siteTwitter,
channel: channel
});
} }
return createBasicMetaTags({ return createBasicMetaTags();
siteDescription: siteDescription,
siteHost: siteHost,
siteTitle: siteTitle,
siteTwitter: siteTwitter,
defaultThumbnail: defaultThumbnail
});
}; };
exports.createMetaTags = createMetaTags; exports.createMetaTags = createMetaTags;

View file

@ -2,10 +2,10 @@ import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, contentType, thumbnail } }) => { const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, contentType, thumbnail } }) => {
const directSourceLink = `asset/${name}/${claimId}`; const embedUrl = `/${claimId}/${name}.${fileExt}`;
const showUrlLink = `/${claimId}/${name}`; const showUrl = `/${claimId}/${name}`;
return ( return (
<Link to={showUrlLink} > <Link to={showUrl} >
{(() => { {(() => {
switch (contentType) { switch (contentType) {
case 'image/jpeg': case 'image/jpeg':
@ -15,7 +15,7 @@ const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, c
return ( return (
<img <img
className={'asset-preview-image'} className={'asset-preview-image'}
src={directSourceLink} src={embedUrl}
alt={name} alt={name}
/> />
); );

View file

@ -9,6 +9,7 @@ class AssetDisplay extends React.Component {
} }
render () { render () {
const { status, error, asset: { claimData: { name, claimId, contentType, fileExt, thumbnail } } } = this.props; const { status, error, asset: { claimData: { name, claimId, contentType, fileExt, thumbnail } } } = this.props;
const sourceUrl = `/${claimId}/${name}.${fileExt}`;
return ( return (
<div className={'asset-display'}> <div className={'asset-display'}>
{(status === LOCAL_CHECK) && {(status === LOCAL_CHECK) &&
@ -39,7 +40,7 @@ class AssetDisplay extends React.Component {
return ( return (
<img <img
className='asset-image' className='asset-image'
src={`/asset/${name}/${claimId}`} src={sourceUrl}
alt={name} alt={name}
/> />
); );
@ -50,14 +51,14 @@ class AssetDisplay extends React.Component {
controls poster={thumbnail} controls poster={thumbnail}
> >
<source <source
src={`/asset/${name}/${claimId}`} src={sourceUrl}
/> />
<p>Your browser does not support the <code>video</code> element.</p> <p>Your browser does not support the <code>video</code> element.</p>
</video> </video>
); );
default: default:
return ( return (
<p>Unsupported file type</p> <p>Unsupported content type</p>
); );
} }
})() })()

View file

@ -50,7 +50,7 @@ class AssetInfo extends React.Component {
content={ content={
<ClickToCopy <ClickToCopy
id={'short-link'} id={'short-link'}
value={`${host}/${shortId}/${name}.${fileExt}`} value={`${host}/${shortId}/${name}`}
/> />
} }
/> />
@ -81,12 +81,12 @@ class AssetInfo extends React.Component {
<Row> <Row>
<SpaceBetween> <SpaceBetween>
<Link <a
className='link--primary' className='link--primary'
to={`/${shortId}/${name}.${fileExt}`} href={`${host}/${claimId}/${name}.${fileExt}`}
> >
Direct Link Direct Link
</Link> </a>
<a <a
className={'link--primary'} className={'link--primary'}
href={`${host}/${claimId}/${name}.${fileExt}`} href={`${host}/${claimId}/${name}.${fileExt}`}

View file

@ -1,16 +1,4 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import View from './view'; import View from './view';
const mapStateToProps = ({ site }) => { export default connect(null, null)(View);
const { defaultDescription, defaultThumbnail, description: siteDescription, host: siteHost, title: siteTitle, twitter: siteTwitter } = site;
return {
defaultDescription,
defaultThumbnail,
siteDescription,
siteHost,
siteTitle,
siteTwitter,
};
};
export default connect(mapStateToProps, null)(View);

View file

@ -2,30 +2,22 @@ import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { createPageTitle } from '../../utils/pageTitle'; import createPageTitle from '../../utils/createPageTitle';
import { createMetaTags } from '../../utils/metaTags'; import createMetaTags from '../../utils/createMetaTags';
import { createCanonicalLink } from '../../utils/canonicalLink'; import createCanonicalLink from '../../utils/createCanonicalLink';
class SEO extends React.Component { class SEO extends React.Component {
render () { render () {
// props from state
const { defaultDescription, defaultThumbnail, siteDescription, siteHost, siteTitle, siteTwitter } = this.props;
// props from parent // props from parent
const { asset, channel, pageUri } = this.props; const { asset, channel, pageUri } = this.props;
let { pageTitle } = this.props; let { pageTitle } = this.props;
// create page title, tags, and canonical link // create page title, tags, and canonical link
pageTitle = createPageTitle(siteTitle, pageTitle); pageTitle = createPageTitle(pageTitle);
const metaTags = createMetaTags({ const metaTags = createMetaTags({
siteDescription,
siteHost,
siteTitle,
siteTwitter,
asset, asset,
channel, channel,
defaultDescription,
defaultThumbnail,
}); });
const canonicalLink = createCanonicalLink(asset, channel, pageUri, siteHost); const canonicalLink = createCanonicalLink(asset, channel, pageUri);
// render results // render results
return ( return (
<Helmet <Helmet

View file

@ -1,29 +0,0 @@
const createBasicCanonicalLink = (page, siteHost) => {
return `${siteHost}/${page}`;
};
const createAssetCanonicalLink = (asset, siteHost) => {
let channelName, certificateId, name, claimId;
if (asset.claimData) {
({ channelName, certificateId, name, claimId } = asset.claimData);
};
if (channelName) {
return `${siteHost}/${channelName}:${certificateId}/${name}`;
};
return `${siteHost}/${claimId}/${name}`;
};
const createChannelCanonicalLink = (channel, siteHost) => {
const { name, longId } = channel;
return `${siteHost}/${name}:${longId}`;
};
export const createCanonicalLink = (asset, channel, page, siteHost) => {
if (asset) {
return createAssetCanonicalLink(asset, siteHost);
}
if (channel) {
return createChannelCanonicalLink(channel, siteHost);
}
return createBasicCanonicalLink(page, siteHost);
};

View file

@ -0,0 +1,93 @@
import siteConfig from '@config/siteConfig.json';
import determineContentTypeFromExtension from './determineContentTypeFromExtension';
import createMetaTagsArray from './createMetaTagsArray';
const {
details: {
host,
title: siteTitle,
twitter,
},
assetDefaults: {
description: defaultDescription,
thumbnail: defaultThumbnail,
},
} = siteConfig;
const VIDEO = 'VIDEO';
const IMAGE = 'IMAGE';
const GIF = 'GIF';
const determineMediaType = (contentType) => {
switch (contentType) {
case 'image/jpg':
case 'image/jpeg':
case 'image/png':
return IMAGE;
case 'image/gif':
return GIF;
case 'video/mp4':
case 'video/webm':
return VIDEO;
default:
return '';
}
};
const createAssetMetaTags = (asset) => {
const { claimData } = asset;
const { contentType } = claimData;
const showUrl = `${host}/${claimData.claimId}/${claimData.name}`;
const serveUrl = `${host}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
const ogTitle = claimData.title || claimData.name;
const ogDescription = claimData.description || defaultDescription;
const ogThumbnailContentType = determineContentTypeFromExtension(claimData.thumbnail);
const ogThumbnail = claimData.thumbnail || defaultThumbnail;
// {property: 'og:title'] = ogTitle},
const metaTags = {
'og:title' : ogTitle,
'twitter:title' : ogTitle,
'og:description' : ogDescription,
'twitter:description': ogDescription,
'og:url' : showUrl,
'og:site_name' : siteTitle,
'twitter:site' : twitter,
'fb:app_id' : '1371961932852223',
};
if (determineMediaType(contentType) === VIDEO) {
const videoEmbedUrl = `${host}/video-embed/${claimData.name}/${claimData.claimId}`;
// card type tags
metaTags['og:type'] = 'video.other';
metaTags['twitter:card'] = 'player';
metaTags['twitter:player'] = videoEmbedUrl;
metaTags['twitter:player:width'] = 600;
metaTags['twitter:text:player_width'] = 600;
metaTags['twitter:player:height'] = 350;
metaTags['twitter:player:stream'] = serveUrl;
metaTags['twitter:player:stream:content_type'] = contentType;
// video tags
metaTags['og:video'] = serveUrl;
metaTags['og:video:secure_url'] = serveUrl;
metaTags['og:video:type'] = contentType;
// image tags
metaTags['og:image'] = ogThumbnail;
metaTags['og:image:width'] = 600;
metaTags['og:image:height'] = 315;
metaTags['og:image:type'] = ogThumbnailContentType;
metaTags['twitter:image'] = ogThumbnail;
} else {
// card type tags
metaTags['og:type'] = 'article';
metaTags['twitter:card'] = 'summary_large_image';
// image tags
metaTags['og:image'] = serveUrl;
metaTags['og:image'] = serveUrl;
metaTags['og:image:width'] = 600;
metaTags['og:image:height'] = 315;
metaTags['og:image:type'] = contentType;
metaTags['twitter:image'] = serveUrl;
}
return createMetaTagsArray(metaTags);
};
export default createAssetMetaTags;

View file

@ -0,0 +1,44 @@
import siteConfig from '@config/siteConfig.json';
import determineContentTypeFromExtension from './determineContentTypeFromExtension.js';
import createMetaTagsArray from './createMetaTagsArray';
const {
details: {
description,
host,
title,
twitter,
},
assetDefaults: {
thumbnail,
},
} = siteConfig;
const createBasicMetaTags = () => {
const metaTags = {
// page details
'og:title' : title,
'twitter:title' : title,
'og:description' : description,
'twitter:description': description,
// url
'og:url' : host,
// site id
'og:site_name' : title,
'twitter:site' : twitter,
'fb:app_id' : '1371961932852223',
// card type
'og:type' : 'article',
'twitter:card' : 'summary_large_image',
// image
'og:image' : thumbnail,
'og:image:width' : 600,
'og:image:height' : 315,
'og:image:type' : determineContentTypeFromExtension(thumbnail),
'twitter:image' : thumbnail,
'twitter:image:alt' : 'Spee.ch Logo',
};
return createMetaTagsArray(metaTags);
};
export default createBasicMetaTags;

View file

@ -0,0 +1,39 @@
import siteConfig from '@config/siteConfig.json';
const {
details: {
host,
},
} = siteConfig;
const createBasicCanonicalLink = (page) => {
return `${host}/${page}`;
};
const createAssetCanonicalLink = (asset) => {
let channelName, certificateId, name, claimId;
if (asset.claimData) {
({ channelName, certificateId, name, claimId } = asset.claimData);
}
if (channelName) {
return `${host}/${channelName}:${certificateId}/${name}`;
}
return `${host}/${claimId}/${name}`;
};
const createChannelCanonicalLink = (channel) => {
const { name, longId } = channel;
return `${host}/${name}:${longId}`;
};
const createCanonicalLink = (asset, channel, page) => {
if (asset) {
return createAssetCanonicalLink(asset);
}
if (channel) {
return createChannelCanonicalLink(channel);
}
return createBasicCanonicalLink(page);
};
export default createCanonicalLink;

View file

@ -0,0 +1,44 @@
import siteConfig from '@config/siteConfig.json';
import determineContentTypeFromExtension from './determineContentTypeFromExtension';
import createMetaTagsArray from './createMetaTagsArray';
const {
details: {
host,
title: siteTitle,
twitter,
},
assetDefaults: {
thumbnail: defaultThumbnail,
},
} = siteConfig;
export const createChannelMetaTags = (channel) => {
const { name, longId } = channel;
const metaTags = {
// page detail tags
'og:title' : `${name} on ${siteTitle}`,
'twitter:title' : `${name} on ${siteTitle}`,
'og:description' : `${name}, a channel on ${siteTitle}`,
'twitter:description': `${name}, a channel on ${siteTitle}`,
// url
'og:url' : `${host}/${name}:${longId}`,
// site info
'og:site_name' : siteTitle,
'twitter:site' : twitter,
'fb:app_id' : '1371961932852223',
// card type tags
'og:type' : 'article',
'twitter:card' : 'summary_large_image',
// image tags
'og:image' : defaultThumbnail,
'og:image:width' : 600,
'og:image:height' : 315,
'og:image:type' : determineContentTypeFromExtension(defaultThumbnail),
'twitter:image' : defaultThumbnail,
'twitter:image:alt' : 'Spee.ch Logo',
};
return createMetaTagsArray(metaTags);
};
export default createChannelMetaTags;

View file

@ -0,0 +1,15 @@
import createAssetMetaTags from './createAssetMetaTags';
import createChannelMetaTags from './createChannelMetaTags.js';
import createBasicMetaTags from './createBasicMetaTags.js';
const createMetaTags = ({ asset, channel }) => {
if (asset) {
return createAssetMetaTags(asset);
}
if (channel) {
return createChannelMetaTags(channel);
}
return createBasicMetaTags();
};
export default createMetaTags;

View file

@ -0,0 +1,14 @@
const createMetaTagsArray = (metaTagsObject) => {
let metaTagsArray = [];
for (let key in metaTagsObject) {
if (metaTagsObject.hasOwnProperty(key)) {
metaTagsArray.push({
property: key,
content : metaTagsObject[key],
});
}
}
return metaTagsArray;
};
export default createMetaTagsArray;

View file

@ -0,0 +1,16 @@
import siteConfig from '@config/siteConfig.json';
const {
details: {
title: siteTitle,
},
} = siteConfig;
const createPageTitle = (pageTitle) => {
if (!pageTitle) {
return `${siteTitle}`;
}
return `${siteTitle} - ${pageTitle}`;
};
export default createPageTitle;

View file

@ -0,0 +1,21 @@
const determineContentTypeFromExtension = (thumbnail) => {
if (thumbnail) {
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
switch (fileExt) {
case 'jpeg':
case 'jpg':
return 'image/jpg';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
case 'mp4':
return 'video/mp4';
default:
return '';
}
}
return '';
};
export default determineContentTypeFromExtension;

View file

@ -1,122 +0,0 @@
const determineOgThumbnailContentType = (thumbnail) => {
if (thumbnail) {
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
switch (fileExt) {
case 'jpeg':
case 'jpg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
case 'mp4':
return 'video/mp4';
default:
return 'image/jpeg';
}
}
return '';
};
const createBasicMetaTags = ({siteHost, siteDescription, siteTitle, siteTwitter, defaultThumbnail}) => {
return [
{property: 'og:title', content: siteTitle},
{property: 'twitter:title', content: siteTitle},
{property: 'og:url', content: siteHost},
{property: 'og:site_name', content: siteTitle},
{property: 'og:description', content: siteDescription},
{property: 'twitter:description', content: siteDescription},
{property: 'twitter:site', content: siteTwitter},
{property: 'twitter:card', content: 'summary_large_image'},
{property: 'og:image', content: defaultThumbnail},
{property: 'twitter:image', content: defaultThumbnail},
{property: 'og:image:type', content: 'image/jpeg'},
];
};
const createChannelMetaTags = ({siteHost, siteTitle, siteTwitter, channel}) => {
const { name, longId } = channel;
return [
{property: 'og:title', content: `${name} on ${siteTitle}`},
{property: 'twitter:title', content: `${name} on ${siteTitle}`},
{property: 'og:url', content: `${siteHost}/${name}:${longId}`},
{property: 'og:site_name', content: siteTitle},
{property: 'og:description', content: `${name}, a channel on ${siteTitle}`},
{property: 'twitter:site', content: siteTwitter},
{property: 'twitter:card', content: 'summary'},
];
};
const createAssetMetaTags = ({siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail}) => {
const { claimData } = asset;
const { contentType } = claimData;
const videoEmbedUrl = `${siteHost}/video-embed/${claimData.name}/${claimData.claimId}`;
const showUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
const source = `${siteHost}/asset/${claimData.name}/${claimData.claimId}`;
const ogTitle = claimData.title || claimData.name;
const ogDescription = claimData.description || defaultDescription;
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
const ogThumbnail = claimData.thumbnail || defaultThumbnail;
const metaTags = [
{property: 'og:title', content: ogTitle},
{property: 'twitter:title', content: ogTitle},
{property: 'og:url', content: showUrl},
{property: 'og:site_name', content: siteTitle},
{property: 'og:description', content: ogDescription},
{property: 'twitter:description', content: ogDescription},
{property: 'og:image:width', content: 600},
{property: 'og:image:height', content: 315},
{property: 'twitter:site', content: siteTwitter},
];
if (contentType === 'video/mp4' || contentType === 'video/webm') {
metaTags.push({property: 'og:video', content: source});
metaTags.push({property: 'og:video:secure_url', content: source});
metaTags.push({property: 'og:video:type', content: contentType});
metaTags.push({property: 'og:image', content: ogThumbnail});
metaTags.push({property: 'twitter:image', content: ogThumbnail});
metaTags.push({property: 'og:image:type', content: ogThumbnailContentType});
metaTags.push({property: 'og:type', content: 'video.other'});
metaTags.push({property: 'twitter:card', content: 'player'});
metaTags.push({property: 'twitter:player', content: videoEmbedUrl});
metaTags.push({property: 'twitter: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:stream', content: source});
metaTags.push({property: 'twitter:player:stream:content_type', content: contentType});
} else {
metaTags.push({property: 'og:image', content: source});
metaTags.push({property: 'twitter:image', content: source});
metaTags.push({property: 'og:image:type', content: contentType});
metaTags.push({property: 'og:type', content: 'article'});
metaTags.push({property: 'twitter:card', content: 'summary_large_image'});
}
return metaTags;
};
export const createMetaTags = ({ siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail }) => {
if (asset) {
return createAssetMetaTags({
siteHost,
siteTitle,
siteTwitter,
asset,
defaultDescription,
defaultThumbnail,
});
}
if (channel) {
return createChannelMetaTags({
siteHost,
siteTitle,
siteTwitter,
channel,
});
}
return createBasicMetaTags({
siteDescription,
siteHost,
siteTitle,
siteTwitter,
defaultThumbnail,
});
};

View file

@ -1,6 +0,0 @@
export const createPageTitle = (siteTitle, pageTitle) => {
if (!pageTitle) {
return `${siteTitle}`;
}
return `${siteTitle} - ${pageTitle}`;
};

View file

@ -27,6 +27,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
ip, ip,
headers, headers,
body, body,
files,
}); });
// check for disabled publishing // check for disabled publishing
if (disabled) { if (disabled) {
@ -36,14 +37,14 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
}); });
} }
// define variables // define variables
let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title; let channelName, channelId, channelPassword, description, fileName, filePath, fileExtension, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title;
// record the start time of the request // record the start time of the request
gaStartTime = Date.now(); gaStartTime = Date.now();
// validate the body and files of the request // validate the body and files of the request
try { try {
// validateApiPublishRequest(body, files); // validateApiPublishRequest(body, files);
({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body));
({fileName, filePath, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files)); ({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files));
({channelName, channelId, channelPassword} = body); ({channelName, channelId, channelPassword} = body);
} catch (error) { } catch (error) {
return res.status(400).json({success: false, message: error.message}); return res.status(400).json({success: false, message: error.message});
@ -76,8 +77,9 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
data : { data : {
name, name,
claimId : result.claim_id, claimId : result.claim_id,
url : `${host}/${result.claim_id}/${name}`, url : `${host}/${result.claim_id}/${name}`, // for backwards compatability with app
embedUrl: `${host}/asset/${name}/${result.claim_id}`, showUrl : `${host}/${result.claim_id}/${name}`,
serveUrl: `${host}/${result.claim_id}/${name}${fileExtension}`,
lbryTx : result, lbryTx : result,
}, },
}); });

View file

@ -1,3 +1,4 @@
const path = require('path');
const validateFileTypeAndSize = require('./validateFileTypeAndSize.js'); const validateFileTypeAndSize = require('./validateFileTypeAndSize.js');
const parsePublishApiRequestFiles = ({file, thumbnail}) => { const parsePublishApiRequestFiles = ({file, thumbnail}) => {
@ -33,6 +34,7 @@ const parsePublishApiRequestFiles = ({file, thumbnail}) => {
return { return {
fileName : file.name, fileName : file.name,
filePath : file.path, filePath : file.path,
fileExtension : path.extname(file.path),
fileType : file.type, fileType : file.type,
thumbnailFileName: (thumbnail ? thumbnail.name : null), thumbnailFileName: (thumbnail ? thumbnail.name : null),
thumbnailFilePath: (thumbnail ? thumbnail.path : null), thumbnailFilePath: (thumbnail ? thumbnail.path : null),

View file

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

View file

@ -1,3 +1,5 @@
const logger = require('winston');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics'); const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js'); const handleShowRender = require('../../../render/build/handleShowRender.js');
@ -6,7 +8,7 @@ const lbryUri = require('../utils/lbryUri.js');
const determineRequestType = require('../utils/determineRequestType.js'); const determineRequestType = require('../utils/determineRequestType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js'); const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
const { EMBED } = require('../constants/request_types.js'); const { SHOW } = require('../constants/request_types.js');
/* /*
@ -16,29 +18,32 @@ const { EMBED } = require('../constants/request_types.js');
const serveByClaim = (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
let hasFileExtension;
try { try {
({ hasFileExtension } = lbryUri.parseModifier(params.claim)); let isChannel, hasFileExtension, claimName;
} catch (error) {
return res.status(400).json({success: false, message: error.message}); ({ isChannel } = lbryUri.parseIdentifier(params.claim));
} if (isChannel) {
// determine request type logger.debug('channel request:', { headers, ip, originalUrl, params });
let requestType = determineRequestType(hasFileExtension, headers);
if (requestType !== EMBED) {
return handleShowRender(req, res); return handleShowRender(req, res);
} }
// parse the claim
let claimName; ({ hasFileExtension } = lbryUri.parseModifier(params.claim));
try { if (determineRequestType(hasFileExtension, headers) === SHOW) {
logger.debug('show request:', { headers, ip, originalUrl, params });
return handleShowRender(req, res);
}
({ claimName } = lbryUri.parseClaim(params.claim)); ({ claimName } = lbryUri.parseClaim(params.claim));
logger.debug('serve request:', { headers, ip, originalUrl, params });
getClaimIdAndServeAsset(null, null, claimName, null, originalUrl, ip, res);
sendGAServeEvent(headers, ip, originalUrl);
} catch (error) { } catch (error) {
return res.status(400).json({success: false, message: error.message}); return res.status(400).json({success: false, message: error.message});
} }
// send google analytics
sendGAServeEvent(headers, ip, originalUrl);
// get the claim Id and then serve the asset
getClaimIdAndServeAsset(null, null, claimName, null, originalUrl, ip, res);
}; };
module.exports = serveByClaim; module.exports = serveByClaim;

View file

@ -1,3 +1,5 @@
const logger = require('winston');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics'); const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js'); const handleShowRender = require('../../../render/build/handleShowRender.js');
@ -7,7 +9,7 @@ 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 { EMBED } = require('../constants/request_types.js'); const { SHOW } = require('../constants/request_types.js');
/* /*
@ -17,40 +19,32 @@ const { EMBED } = require('../constants/request_types.js');
const serverByIdentifierAndClaim = (req, res) => { const serverByIdentifierAndClaim = (req, res) => {
const { headers, ip, originalUrl, params } = req; const { headers, ip, originalUrl, params } = req;
// parse request
let hasFileExtension;
try { try {
let hasFileExtension, claimName, isChannel, channelName, channelClaimId, claimId;
({ hasFileExtension } = lbryUri.parseModifier(params.claim)); ({ hasFileExtension } = lbryUri.parseModifier(params.claim));
} catch (error) { if (determineRequestType(hasFileExtension, headers) === SHOW) {
return res.status(400).json({success: false, message: error.message}); logger.debug('show request:', { headers, ip, originalUrl, params });
}
// determine request type
let requestType = determineRequestType(hasFileExtension, headers);
if (requestType !== EMBED) {
return handleShowRender(req, res); return handleShowRender(req, res);
} }
// parse the claim
let claimName;
try {
({ claimName } = lbryUri.parseClaim(params.claim)); ({ claimName } = lbryUri.parseClaim(params.claim));
} catch (error) {
return res.status(400).json({success: false, message: error.message});
}
// parse the identifier
let isChannel, channelName, channelClaimId, claimId;
try {
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(params.identifier)); ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(params.identifier));
} catch (error) {
return res.status(400).json({success: false, message: error.message});
}
// for backwards compatability, flip claim name and claim id if necessary
if (!isChannel) { if (!isChannel) {
[claimId, claimName] = flipClaimNameAndId(claimId, claimName); [claimId, claimName] = flipClaimNameAndId(claimId, claimName);
} }
// send google analytics
sendGAServeEvent(headers, ip, originalUrl); logger.debug('serve request:', { headers, ip, originalUrl, params });
// 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);
sendGAServeEvent(headers, ip, originalUrl);
} catch (error) {
return res.status(400).json({success: false, message: error.message});
}
}; };
module.exports = serverByIdentifierAndClaim; module.exports = serverByIdentifierAndClaim;

View file

@ -1,60 +1,16 @@
const logger = require('winston'); const { SERVE, SHOW } = require('../constants/request_types.js');
const { EMBED, BROWSER, SOCIAL } = require('../constants/request_types.js');
function headersMatchesSocialBotList (headers) {
const userAgent = headers['user-agent'];
const socialBotList = [
'facebookexternalhit',
'Twitterbot',
];
for (let i = 0; i < socialBotList.length; i++) {
if (userAgent.includes(socialBotList[i])) {
logger.debug('request is from social bot:', socialBotList[i]);
return true;
}
}
return false;
}
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}) { function clientWantsAsset ({accept, range}) {
const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/) && !accept.match(/text\/\*/); const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/);
const videoIsWanted = accept && range; const videoIsWanted = accept && accept.match(/video\/.*/) && !accept.match(/text\/html/);
return imageIsWanted || videoIsWanted; return imageIsWanted || videoIsWanted;
} }
const determineRequestType = (hasFileExtension, headers) => { const determineRequestType = (hasFileExtension, headers) => {
let responseType; if (hasFileExtension || clientWantsAsset(headers)) {
logger.debug('headers:', headers); return SERVE;
// return early with 'show' if headers match the list
if (headersMatchesSocialBotList(headers)) {
return SOCIAL;
} }
// if request is not from a social bot... return SHOW;
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; module.exports = determineRequestType;

View file

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

View file

@ -1,4 +1,4 @@
<video controls> <video controls>
<source src="{{host}}/asset/{{name}}/{{claimId}}" type="video/mp4"> <source src="{{host}}/{{claimId}}/{{name}}.mp4" type="video/mp4">
Your browser does not support video Your browser does not support video
</video> </video>